root/src/modules/http.c

Revision 5229ef1f0c47697846621aa99c2b3836b192a769, 27.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

use the correct Host:

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  */
5
6 #include "noit_defines.h"
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <assert.h>
12 #include <math.h>
13
14 #include <libxml/parser.h>
15 #include <libxml/tree.h>
16 #include <libxml/xpath.h>
17
18 #include "noit_module.h"
19 #include "noit_check.h"
20 #include "utils/noit_log.h"
21 #include "utils/noit_hash.h"
22
23 #include <apr_uri.h>
24 #include <apr_atomic.h>
25 #include <apr_strings.h>
26 #include "serf.h"
27
28 #define NOIT_HTTP_VERSION_STRING "0.1"
29
30 typedef struct {
31   noit_hash_table *options;
32   void (*results)(noit_module_t *, noit_check_t *);
33 } serf_module_conf_t;
34
35 typedef struct {
36   int using_ssl;
37   serf_ssl_context_t *ssl_ctx;
38   serf_bucket_alloc_t *bkt_alloc;
39 } app_baton_t;
40
41 typedef struct {
42   serf_response_acceptor_t acceptor;
43   app_baton_t *acceptor_baton;
44
45   serf_response_handler_t handler;
46   const char *host;
47   const char *method;
48   const char *path;
49   const char *authn;
50
51   noit_module_t *self;
52   noit_check_t *check;
53 } handler_baton_t;
54
55 typedef struct buf_t {
56   char *b;
57   int l;
58 } buf_t;
59
60 typedef struct {
61   apr_pool_t *pool;
62   apr_sockaddr_t *address;
63   serf_context_t *context;
64   serf_connection_t *connection;
65   serf_request_t *request;
66   app_baton_t app_ctx;
67   handler_baton_t handler_ctx;
68   apr_uri_t url;
69   int timed_out;
70
71   serf_status_line status;
72   buf_t headers;
73   buf_t body;
74
75   struct timeval finish_time;
76   eventer_t fd_event;
77   eventer_t timeout_event;
78 } serf_check_info_t;
79
80 typedef struct {
81   serf_check_info_t serf;
82   struct timeval xml_doc_time;
83   char *xpathexpr;
84   xmlDocPtr xml_doc;
85   char *resmod;
86   char *resserv;
87 } resmon_check_info_t;
88
89 typedef struct {
90   noit_module_t *self;
91   noit_check_t *check;
92   void *serf_baton;
93   apr_socket_t *skt;
94 } serf_closure_t;
95
96 static noit_log_stream_t nlerr = NULL;
97 static noit_log_stream_t nldeb = NULL;
98 static int serf_handler(eventer_t e, int mask, void *closure,
99                         struct timeval *now);
100 static int serf_recur_handler(eventer_t e, int mask, void *closure,
101                               struct timeval *now);
102 static void serf_log_results(noit_module_t *self, noit_check_t *check);
103 static void resmon_log_results(noit_module_t *self, noit_check_t *check);
104 static void resmon_part_log_results(noit_module_t *self, noit_check_t *check,
105                                     noit_check_t *parent);
106
107 static int serf_config(noit_module_t *self, noit_hash_table *options) {
108   serf_module_conf_t *conf;
109   conf = noit_module_get_userdata(self);
110   if(conf) {
111     if(conf->options) {
112       noit_hash_destroy(conf->options, free, free);
113       free(conf->options);
114     }
115   }
116   else
117     conf = calloc(1, sizeof(*conf));
118   conf->options = options;
119   conf->results = serf_log_results;
120   noit_module_set_userdata(self, conf);
121   return 0;
122 }
123 static int resmon_config(noit_module_t *self, noit_hash_table *options) {
124   serf_module_conf_t *conf;
125   conf = noit_module_get_userdata(self);
126   if(conf) {
127     if(conf->options) {
128       noit_hash_destroy(conf->options, free, free);
129       free(conf->options);
130     }
131   }
132   else
133     conf = calloc(1, sizeof(*conf));
134   conf->options = options;
135   if(!conf->options) conf->options = calloc(1, sizeof(*conf->options));
136   noit_hash_store(conf->options, strdup("url"), strlen("url"),
137                   strdup("http://localhost:81/"));
138   conf->results = resmon_log_results;
139   noit_module_set_userdata(self, conf);
140   return 0;
141 }
142 static void generic_log_results(noit_module_t *self, noit_check_t *check) {
143   serf_module_conf_t *module_conf;
144   module_conf = noit_module_get_userdata(self);
145   module_conf->results(self, check);
146 }
147 static void serf_log_results(noit_module_t *self, noit_check_t *check) {
148   serf_check_info_t *ci = check->closure;
149   struct timeval duration;
150   stats_t current;
151   int expect_code = 200;
152   char *code_str;
153   char human_buffer[256], code[4], rt[14];
154
155   memset(&current, 0, sizeof(current));
156
157   if(noit_hash_retrieve(check->config, "code", strlen("code"),
158                         (void **)&code_str))
159     expect_code = atoi(code_str);
160
161   sub_timeval(ci->finish_time, check->last_fire_time, &duration);
162
163   snprintf(code, sizeof(code), "%3d", ci->status.code);
164   snprintf(rt, sizeof(rt), "%.3fms",
165            (float)duration.tv_sec + (float)duration.tv_usec / 1000000.0);
166   snprintf(human_buffer, sizeof(human_buffer),
167            "code=%s,rt=%s,bytes=%d",
168            ci->status.code ? code : "undefined",
169            ci->timed_out ? "timeout" : rt,
170            ci->body.l);
171   noitL(nldeb, "http(%s) [%s]\n", check->target, human_buffer);
172
173   memcpy(&current.whence, &ci->finish_time, sizeof(current.whence));
174   current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
175   current.available = (ci->timed_out || !ci->status.code) ? NP_UNAVAILABLE : NP_AVAILABLE;
176   current.state = (ci->status.code != 200) ? NP_BAD : NP_GOOD;
177   current.status = human_buffer;
178   if(current.available == NP_AVAILABLE) {
179     noit_stats_set_metric_string(&current, "code", ci->status.code?code:NULL);
180     noit_stats_set_metric_int(&current, "bytes", &ci->body.l);
181   }
182   else {
183     noit_stats_set_metric_int(&current, "code", NULL);
184     noit_stats_set_metric_int(&current, "bytes", NULL);
185   }
186   noit_check_set_stats(self, check, &current);
187 }
188 static void resmon_part_log_results_xml(noit_module_t *self,
189                                         noit_check_t *check,
190                                         xmlDocPtr xml) {
191   resmon_check_info_t *rci = check->closure;
192   xmlXPathContextPtr xpath_ctxt = NULL;
193   stats_t current;
194
195   memset(&current, 0, sizeof(current));
196   current.available = NP_UNAVAILABLE;
197   current.state = NP_BAD;
198
199   if(xml && rci->xpathexpr) {
200     current.available = NP_AVAILABLE;
201     xpath_ctxt = xmlXPathNewContext(xml);
202     if(xpath_ctxt) {
203       xmlXPathObjectPtr pobj;
204       pobj = xmlXPathEval((xmlChar *)rci->xpathexpr, xpath_ctxt);
205       if(pobj) {
206         int i, cnt;
207         cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
208         for(i=0; i<cnt; i++) {
209           xmlNodePtr node;
210           char *value;
211           node = xmlXPathNodeSetItem(pobj->nodesetval, i);
212           value = (char *)xmlXPathCastNodeToString(node);
213           if(!strcmp((char *)node->name,"last_runtime_seconds")) {
214             float duration = atof(value) * 1000;
215             current.duration = (int) duration;
216           }
217           else if(!strcmp((char *)node->name, "message")) {
218             current.status = strdup(value);
219           }
220           else if(!strcmp((char *)node->name, "state")) {
221             current.state = strcmp(value,"OK") ? NP_BAD : NP_GOOD;
222           }
223         }
224       }
225     }
226   }
227   memcpy(&current.whence, &rci->serf.finish_time, sizeof(current.whence));
228   current.status = current.status ? current.status : strdup("unknown");
229   noitL(nldeb, "resmon_part(%s/%s/%s) [%s]\n", check->target,
230         rci->resmod, rci->resserv, current.status);
231   noit_check_set_stats(self, check, &current);
232 }
233 static void resmon_part_log_results(noit_module_t *self, noit_check_t *check,
234                                     noit_check_t *parent) {
235   resmon_check_info_t *rci = parent->closure;
236   resmon_part_log_results_xml(self, check, rci->xml_doc);
237 }
238 static void resmon_log_results(noit_module_t *self, noit_check_t *check) {
239   serf_check_info_t *ci = check->closure;
240   resmon_check_info_t *rci = check->closure;
241   struct timeval duration;
242   stats_t current;
243   int services = 0;
244   char human_buffer[256], rt[14];
245   xmlDocPtr resmon_results = NULL;
246   xmlXPathContextPtr xpath_ctxt = NULL;
247   xmlXPathObjectPtr pobj = NULL;
248
249   memset(&current, 0, sizeof(current));
250
251   if(ci->body.b) resmon_results = xmlParseMemory(ci->body.b, ci->body.l);
252   if(resmon_results) {
253     xpath_ctxt = xmlXPathNewContext(resmon_results);
254     pobj = xmlXPathEval((xmlChar *)"/ResmonResults/ResmonResult", xpath_ctxt);
255     if(pobj)
256       if(pobj->type == XPATH_NODESET)
257         services = xmlXPathNodeSetGetLength(pobj->nodesetval);
258   } else {
259     noitL(nlerr, "Error in resmon doc: %s\n", ci->body.b);
260   }
261
262   /* Save our results for future dependent checks */
263   memcpy(&current.whence, &ci->finish_time, sizeof(current.whence));
264   memcpy(&rci->xml_doc_time, &ci->finish_time, sizeof(ci->finish_time));
265   if(rci->xml_doc) xmlFreeDoc(rci->xml_doc);
266   rci->xml_doc = resmon_results;
267
268   if(rci->xpathexpr) {
269     /* This is actually a part check... we had to do all the work as
270      * it isn't being used as a causal firing from a generic resmon check
271      */
272     resmon_part_log_results_xml(self, check, rci->xml_doc);
273     goto out;
274   }
275
276   sub_timeval(ci->finish_time, check->last_fire_time, &duration);
277   snprintf(rt, sizeof(rt), "%.3fms",
278            (float)duration.tv_sec + (float)duration.tv_usec / 1000000.0);
279   snprintf(human_buffer, sizeof(human_buffer),
280            "services=%d,rt=%s",
281            services,
282            ci->timed_out ? "timeout" : rt);
283   noitL(nldeb, "resmon(%s) [%s]\n", check->target, human_buffer);
284
285   current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
286   current.available = (ci->timed_out || ci->status.code != 200) ?
287                           NP_UNAVAILABLE : NP_AVAILABLE;
288   current.state = services ? NP_GOOD : NP_BAD;
289   current.status = human_buffer;
290
291   noit_stats_set_metric_int(&current, "services", &services);
292   if(services) {
293     int i;
294     for(i=0; i<services; i++) {
295       xmlNodePtr node, attrnode;
296       node = xmlXPathNodeSetItem(pobj->nodesetval, i);
297       if(node) {
298         int a;
299         char *attrs[3] = { "last_runtime_seconds", "state", "message" };
300         char *resmod = NULL, *resserv = NULL, *value = NULL;
301         char attr[1024];
302         xmlXPathObjectPtr sobj;
303
304         xpath_ctxt->node = node;
305         sobj = xmlXPathEval((xmlChar *)"@module", xpath_ctxt);
306         resmod = (char *)xmlXPathCastNodeSetToString(sobj->nodesetval);
307         sobj = xmlXPathEval((xmlChar *)"@service", xpath_ctxt);
308         resserv = (char *)xmlXPathCastNodeSetToString(sobj->nodesetval);
309         if(!resmod && !resserv) continue;
310
311         for(a=0; a<3; a++) {
312           int intval;
313           sobj = xmlXPathEval((xmlChar *)attrs[a], xpath_ctxt);
314           attrnode = xmlXPathNodeSetItem(sobj->nodesetval, 0);
315           value = (char *)xmlXPathCastNodeToString(attrnode);
316           snprintf(attr, sizeof(attr), "%s`%s`%s",
317                    resmod, resserv, (char *)attrnode->name);
318           switch(a) {
319             case 0:
320               /* The first is integer */
321               intval = (int)(atof(value) * 1000.0);
322               noit_stats_set_metric_int(&current, attr, &intval);
323               break;
324             case 1:
325             case 2:
326               noit_stats_set_metric_string(&current, attr, (char *)value);
327               break;
328           }
329         }
330       }
331     }
332   }
333
334   noit_check_set_stats(self, check, &current);
335
336  out:
337   if(pobj) xmlXPathFreeObject(pobj);
338   if(xpath_ctxt) xmlXPathFreeContext(xpath_ctxt);
339 }
340 static int serf_complete(eventer_t e, int mask,
341                          void *closure, struct timeval *now) {
342   serf_closure_t *ccl = (serf_closure_t *)closure;
343   serf_check_info_t *ci = (serf_check_info_t *)ccl->check->closure;
344
345   noitLT(nldeb, now, "serf_complete(%s)\n", ccl->check->target);
346   generic_log_results(ccl->self, ccl->check);
347   if(ci->connection) {
348     serf_connection_close(ci->connection);
349     ci->connection = NULL;
350   }
351   if(ci->fd_event) {
352     eventer_remove_fd(ci->fd_event->fd);
353     eventer_free(ci->fd_event);
354     ci->fd_event = NULL;
355   }
356   ci->timeout_event = NULL;
357   apr_pool_destroy(ci->pool);
358   memset(ci, 0, sizeof(*ci));
359   ccl->check->flags &= ~NP_RUNNING;
360   free(ccl);
361   return 0;
362 }
363
364 static int serf_handler(eventer_t e, int mask,
365                         void *closure, struct timeval *now) {
366   apr_pollfd_t desc = { 0 };
367   serf_closure_t *sct = closure;
368   serf_check_info_t *ci = sct->check->closure;
369
370   desc.desc_type = APR_POLL_SOCKET;
371   desc.desc.s = sct->skt;
372
373   desc.rtnevents = 0;
374   if(mask & EVENTER_READ) desc.rtnevents |= APR_POLLIN;
375   if(mask & EVENTER_WRITE) desc.rtnevents |= APR_POLLOUT;
376   if(mask & EVENTER_EXCEPTION) desc.rtnevents |= APR_POLLERR;
377   serf_event_trigger(ci->context, sct->serf_baton, &desc);
378   serf_context_prerun(ci->context);
379
380   /* We're about to deschedule and free the event, drop our reference */
381   if(!e->mask)
382     ci->fd_event = NULL;
383
384   return e->mask;
385 }
386
387 static int serf_init(noit_module_t *self) {
388   return 0;
389 }
390 static void closed_connection(serf_connection_t *conn,
391                               void *closed_baton,
392                               apr_status_t why,
393                               apr_pool_t *pool) {
394 }
395
396 static serf_bucket_t* conn_setup(apr_socket_t *skt,
397                                 void *setup_baton,
398                                 apr_pool_t *pool) {
399   serf_bucket_t *c;
400   app_baton_t *ctx = setup_baton;
401
402   c = serf_bucket_socket_create(skt, ctx->bkt_alloc);
403   if (ctx->using_ssl) {
404       c = serf_bucket_ssl_decrypt_create(c, ctx->ssl_ctx, ctx->bkt_alloc);
405       if (!ctx->ssl_ctx) {
406           ctx->ssl_ctx = serf_bucket_ssl_decrypt_context_get(c);
407       }
408   }
409
410   return c;
411 }
412
413 static serf_bucket_t* accept_response(serf_request_t *request,
414                                       serf_bucket_t *stream,
415                                       void *acceptor_baton,
416                                       apr_pool_t *pool) {
417   serf_bucket_t *c;
418   serf_bucket_alloc_t *bkt_alloc;
419
420   /* get the per-request bucket allocator */
421   bkt_alloc = serf_request_get_alloc(request);
422
423   /* Create a barrier so the response doesn't eat us! */
424   c = serf_bucket_barrier_create(stream, bkt_alloc);
425
426   return serf_bucket_response_create(c, bkt_alloc);
427 }
428
429 static void append_buf(apr_pool_t *p, buf_t *b,
430                        const char *data, int len) {
431   char *n;
432   n = apr_palloc(p, b->l + len + 1);
433   if(b->l == 0)
434     b->b = n;
435   else {
436     memcpy(n, b->b, b->l);
437     b->b = n;
438   }
439   memcpy(b->b + b->l, data, len);
440   b->l += len;
441   b->b[b->l] = '\0';
442 }
443
444 static apr_status_t handle_response(serf_request_t *request,
445                                     serf_bucket_t *response,
446                                     void *handler_baton,
447                                     apr_pool_t *pool) {
448   const char *data;
449   apr_size_t len;
450   apr_status_t status;
451   handler_baton_t *ctx = handler_baton;
452   serf_check_info_t *ci = ctx->check->closure;
453
454   if(response == NULL) {
455     /* We were cancelled. */
456     goto finish;
457   }
458   status = serf_bucket_response_status(response, &ci->status);
459   if (status) {
460     if (APR_STATUS_IS_EAGAIN(status)) {
461       return status;
462     }
463     goto finish;
464   }
465
466   while (1) {
467     status = serf_bucket_read(response, 1024*32, &data, &len);
468     if (SERF_BUCKET_READ_ERROR(status))
469       return status;
470
471     append_buf(ci->pool, &ci->body, data, len);
472
473     /* are we done yet? */
474     if (APR_STATUS_IS_EOF(status)) {
475       serf_bucket_t *hdrs;
476       hdrs = serf_bucket_response_get_headers(response);
477       while (1) {
478         status = serf_bucket_read(hdrs, 2048, &data, &len);
479         if (SERF_BUCKET_READ_ERROR(status))
480           return status;
481
482         append_buf(ci->pool, &ci->headers, data, len);
483         if (APR_STATUS_IS_EOF(status)) {
484           break;
485         }
486       }
487
488       goto finish;
489     }
490
491     /* have we drained the response so far? */
492     if (APR_STATUS_IS_EAGAIN(status))
493       return status;
494
495     /* loop to read some more. */
496   }
497  finish:
498   gettimeofday(&ci->finish_time, NULL);
499   if(ci->timeout_event) {
500     eventer_remove(ci->timeout_event);
501     ci->timed_out = 0;
502     memcpy(&ci->timeout_event->whence, &ci->finish_time,
503            sizeof(&ci->finish_time));
504     eventer_add(ci->timeout_event);
505   }
506   return APR_EOF;
507 }
508
509 static apr_status_t setup_request(serf_request_t *request,
510                                   void *setup_baton,
511                                   serf_bucket_t **req_bkt,
512                                   serf_response_acceptor_t *acceptor,                                             void **acceptor_baton,
513                                   serf_response_handler_t *handler,
514                                   void **handler_baton,
515                                   apr_pool_t *pool) {
516   handler_baton_t *ctx = setup_baton;
517   serf_bucket_t *hdrs_bkt;
518   serf_bucket_t *body_bkt;
519
520   body_bkt = NULL;
521
522   *req_bkt = serf_bucket_request_create(ctx->method, ctx->path, body_bkt,
523                                         serf_request_get_alloc(request));
524
525   hdrs_bkt = serf_bucket_request_get_headers(*req_bkt);
526
527   serf_bucket_headers_setn(hdrs_bkt, "Host", ctx->host);
528   serf_bucket_headers_setn(hdrs_bkt, "User-Agent",
529                            "Noit/" NOIT_HTTP_VERSION_STRING);
530   /* Shouldn't serf do this for us? */
531   serf_bucket_headers_setn(hdrs_bkt, "Accept-Encoding", "gzip");
532
533   if (ctx->authn != NULL) {
534     serf_bucket_headers_setn(hdrs_bkt, "Authorization", ctx->authn);
535   }
536
537   if (ctx->acceptor_baton->using_ssl) {
538     serf_bucket_alloc_t *req_alloc;
539     app_baton_t *app_ctx = ctx->acceptor_baton;
540
541     req_alloc = serf_request_get_alloc(request);
542
543     if (app_ctx->ssl_ctx == NULL) {
544       *req_bkt =
545         serf_bucket_ssl_encrypt_create(*req_bkt, NULL,
546                                        app_ctx->bkt_alloc);
547       app_ctx->ssl_ctx =
548         serf_bucket_ssl_encrypt_context_get(*req_bkt);
549     }
550     else {
551       *req_bkt =
552         serf_bucket_ssl_encrypt_create(*req_bkt, app_ctx->ssl_ctx,
553                                        app_ctx->bkt_alloc);
554     }
555   }
556
557   *acceptor = ctx->acceptor;
558   *acceptor_baton = ctx->acceptor_baton;
559   *handler = ctx->handler;
560   *handler_baton = ctx;
561
562   return APR_SUCCESS;
563 }
564
565 struct __unix_apr_socket_t {
566   apr_pool_t *pool;
567   int socketdes;
568 };
569
570 static apr_status_t serf_eventer_add(void *user_baton,
571                                      apr_pollfd_t *pfd,
572                                      void *serf_baton) {
573   eventer_t e, newe = NULL;
574   serf_closure_t *sct = user_baton, *newsct;
575   assert(pfd->desc_type == APR_POLL_SOCKET);
576   struct __unix_apr_socket_t *hack = (struct __unix_apr_socket_t *)pfd->desc.s;
577
578   noitL(nldeb, "serf_eventer_add() => %d\n", hack->socketdes);
579   e = eventer_find_fd(hack->socketdes);
580   if(!e) {
581     newe = e = eventer_alloc();
582     e->fd = hack->socketdes;
583     e->callback = serf_handler;
584     e->closure = calloc(1, sizeof(serf_closure_t));
585   }
586   newsct = e->closure;
587   newsct->self = sct->self;
588   newsct->check = sct->check;
589   newsct->serf_baton = serf_baton;
590   newsct->skt = pfd->desc.s;
591   e->mask = 0;
592   if(pfd->reqevents & APR_POLLIN) e->mask |= EVENTER_READ;
593   if(pfd->reqevents & APR_POLLOUT) e->mask |= EVENTER_WRITE;
594   if(pfd->reqevents & APR_POLLERR) e->mask |= EVENTER_EXCEPTION;
595   if(newe) {
596     serf_check_info_t *ci = sct->check->closure;
597     eventer_add(newe);
598     ci->fd_event = newe;
599   }
600 /* ** Unneeded as this is called recursively **
601   else
602     eventer_update(e);
603 */
604   return APR_SUCCESS;
605 }
606 static apr_status_t serf_eventer_remove(void *user_baton,
607                                         apr_pollfd_t *pfd,
608                                         void *serf_baton) {
609   serf_closure_t *sct = user_baton;
610   serf_check_info_t *ci;
611   eventer_t e;
612
613   ci = sct->check->closure;
614   assert(pfd->desc_type == APR_POLL_SOCKET);
615   struct __unix_apr_socket_t *hack = (struct __unix_apr_socket_t *)pfd->desc.s;
616
617   noitL(nldeb, "serf_eventer_remove() => %d\n", hack->socketdes);
618   e = eventer_find_fd(hack->socketdes);
619   if(e) e->mask = 0;
620   return 0;
621 }
622
623 static int serf_initiate(noit_module_t *self, noit_check_t *check) {
624   serf_closure_t *ccl;
625   serf_check_info_t *ci;
626   struct timeval when, p_int;
627   apr_status_t status;
628   eventer_t newe;
629   serf_module_conf_t *mod_config;
630   char *config_url;
631
632   mod_config = noit_module_get_userdata(self);
633   ci = (serf_check_info_t *)check->closure;
634   /* We cannot be running */
635   assert(!(check->flags & NP_RUNNING));
636   check->flags |= NP_RUNNING;
637   noitL(nldeb, "serf_initiate(%p,%s)\n",
638         self, check->target);
639
640   /* remove a timeout if we still have one -- we should unless someone
641    * has set a lower timeout than the period.
642    */
643   ci->timed_out = 1;
644   if(ci->timeout_event) {
645     eventer_remove(ci->timeout_event);
646     free(ci->timeout_event->closure);
647     eventer_free(ci->timeout_event);
648     ci->timeout_event = NULL;
649   }
650   assert(!ci->pool);
651   apr_pool_create(&ci->pool, NULL);
652   apr_atomic_init(ci->pool);
653
654   gettimeofday(&when, NULL);
655   memcpy(&check->last_fire_time, &when, sizeof(when));
656
657   ccl = apr_pcalloc(ci->pool, sizeof(*ccl));
658   ccl->self = self;
659   ccl->check = check;
660
661   if(!noit_hash_retrieve(check->config, "url", strlen("url"),
662                         (void **)&config_url))
663     if(!mod_config->options ||
664        !noit_hash_retrieve(mod_config->options, "url", strlen("url"),
665                            (void **)&config_url))
666       config_url = "http://localhost/";
667   apr_uri_parse(ci->pool, config_url, &ci->url);
668
669   if (!ci->url.port) {
670     ci->url.port = apr_uri_port_of_scheme(ci->url.scheme);
671   }
672   if (!ci->url.path) {
673     ci->url.path = "/";
674   }
675
676   if (strcasecmp(ci->url.scheme, "https") == 0) {
677     ci->app_ctx.using_ssl = 1;
678   }
679   else {
680     ci->app_ctx.using_ssl = 0;
681   }
682
683   status = apr_sockaddr_info_get(&ci->address,
684                                  check->target, APR_UNSPEC, ci->url.port, 0,
685                                  ci->pool);
686   if (status) {
687     /* Handle error -- log failure */
688     apr_pool_destroy(ci->pool);
689     memset(ci, 0, sizeof(*ci));
690     check->flags &= ~NP_RUNNING;
691     return 0;
692   }
693
694   ci->context = serf_context_create_ex(ccl, serf_eventer_add,
695                                        serf_eventer_remove, ci->pool);
696
697   ci->app_ctx.bkt_alloc = serf_bucket_allocator_create(ci->pool, NULL, NULL);
698   ci->app_ctx.ssl_ctx = NULL;
699
700   ci->connection = serf_connection_create(ci->context, ci->address,
701                                           conn_setup, &ci->app_ctx,
702                                           closed_connection, &ci->app_ctx,
703                                           ci->pool);
704
705   ci->handler_ctx.method = apr_pstrdup(ci->pool, "GET");
706   ci->handler_ctx.host = apr_pstrdup(ci->pool, ci->url.hostname);
707   ci->handler_ctx.path = ci->url.path;
708   ci->handler_ctx.authn = NULL;
709
710   ci->handler_ctx.acceptor = accept_response;
711   ci->handler_ctx.acceptor_baton = &ci->app_ctx;
712   ci->handler_ctx.handler = handle_response;
713   ci->handler_ctx.self = self;
714   ci->handler_ctx.check = check;
715
716   ci->request = serf_connection_request_create(ci->connection, setup_request,
717                                                &ci->handler_ctx);
718   serf_context_prerun(ci->context);
719
720   newe = eventer_alloc();
721   newe->mask = EVENTER_TIMER;
722   gettimeofday(&when, NULL);
723   p_int.tv_sec = check->timeout / 1000;
724   p_int.tv_usec = (check->timeout % 1000) * 1000;
725   add_timeval(when, p_int, &newe->whence);
726   ccl = calloc(1, sizeof(*ccl));
727   ccl->self = self;
728   ccl->check = check;
729   newe->closure = ccl;
730   newe->callback = serf_complete;
731   eventer_add(newe);
732   ci->timeout_event = newe;
733   return 0;
734 }
735 static int serf_schedule_next(noit_module_t *self,
736                               struct timeval *last_check, noit_check_t *check,
737                               struct timeval *now) {
738   eventer_t newe;
739   struct timeval period, earliest;
740   serf_closure_t *ccl;
741
742   if(check->period == 0) return 0;
743
744   /* If we have an event, we know when we intended it to fire.  This means
745    * we should schedule that point + period.
746    */
747   if(now)
748     memcpy(&earliest, now, sizeof(earliest));
749   else
750     gettimeofday(&earliest, NULL);
751   period.tv_sec = check->period / 1000;
752   period.tv_usec = (check->period % 1000) * 1000;
753
754   newe = eventer_alloc();
755   memcpy(&newe->whence, last_check, sizeof(*last_check));
756   add_timeval(newe->whence, period, &newe->whence);
757   if(compare_timeval(newe->whence, earliest) < 0)
758     memcpy(&newe->whence, &earliest, sizeof(earliest));
759   newe->mask = EVENTER_TIMER;
760   newe->callback = serf_recur_handler;
761   ccl = calloc(1, sizeof(*ccl));
762   ccl->self = self;
763   ccl->check = check;
764   newe->closure = ccl;
765
766   eventer_add(newe);
767   check->fire_event = newe;
768   return 0;
769 }
770 static int serf_recur_handler(eventer_t e, int mask, void *closure,
771                               struct timeval *now) {
772   serf_closure_t *cl = (serf_closure_t *)closure;
773   serf_schedule_next(cl->self, &e->whence, cl->check, now);
774   serf_initiate(cl->self, cl->check);
775   free(cl);
776   return 0;
777 }
778 static int serf_initiate_check(noit_module_t *self, noit_check_t *check,
779                                int once, noit_check_t *cause) {
780   if(!check->closure) check->closure = calloc(1, sizeof(serf_check_info_t));
781   if(once) {
782     serf_initiate(self, check);
783     return 0;
784   }
785   /* If check->fire_event, we're already scheduled... */
786   if(!check->fire_event) {
787     struct timeval epoch = { 0L, 0L };
788     noit_check_fake_last_check(check, &epoch, NULL);
789     serf_schedule_next(self, &epoch, check, NULL);
790   }
791   return 0;
792 }
793 static int resmon_initiate_check(noit_module_t *self, noit_check_t *check,
794                                  int once, noit_check_t *parent) {
795   /* resmon_check_info_t gives us a bit more space */
796   if(!check->closure) check->closure = calloc(1, sizeof(resmon_check_info_t));
797   if(once) {
798     serf_initiate(self, check);
799     return 0;
800   }
801   if(!check->fire_event) {
802     struct timeval epoch = { 0L, 0L };
803     noit_check_fake_last_check(check, &epoch, NULL);
804     serf_schedule_next(self, &epoch, check, NULL);
805   }
806   return 0;
807 }
808
809 static int resmon_part_initiate_check(noit_module_t *self, noit_check_t *check,
810                                       int once, noit_check_t *parent) {
811   char xpathexpr[1024];
812   const char *resmod, *resserv;
813   resmon_check_info_t *rci;
814
815   if(!check->closure) check->closure = calloc(1, sizeof(resmon_check_info_t));
816   rci = check->closure;
817   if(!rci->xpathexpr) {
818     if(!noit_hash_retrieve(check->config,
819                            "resmon_module", strlen("resmon_module"),
820                            (void **)&resmod)) {
821       resmod = "DUMMY_MODULE";
822     }
823     if(!noit_hash_retrieve(check->config,
824                            "resmon_service", strlen("resmon_service"),
825                            (void **)&resserv)) {
826       resserv = "DUMMY_SERVICE";
827     }
828     snprintf(xpathexpr, sizeof(xpathexpr),
829              "//ResmonResult[@module=\"%s\" and @service=\"%s\"]/*",
830              resmod, resserv);
831     rci->xpathexpr = strdup(xpathexpr);
832     rci->resmod = strdup(resmod);
833     rci->resserv = strdup(resserv);
834   }
835
836   if(parent && !strcmp(parent->module, "resmon")) {
837     /* Content is cached in the parent */
838     resmon_part_log_results(self, check, parent);
839     return 0;
840   }
841   if(once) {
842     serf_initiate(self, check);
843     return 0;
844   }
845   if(!check->fire_event)
846     serf_schedule_next(self, NULL, check, NULL);
847   return 0;
848 }
849
850
851
852 static int serf_onload(noit_module_t *self) {
853   apr_initialize();
854   atexit(apr_terminate);
855
856   nlerr = noit_log_stream_find("error/serf");
857   nldeb = noit_log_stream_find("debug/serf");
858   if(!nlerr) nlerr = noit_stderr;
859   if(!nldeb) nldeb = noit_debug;
860
861   eventer_name_callback("http/serf_handler", serf_handler);
862   eventer_name_callback("http/serf_complete", serf_complete);
863   eventer_name_callback("http/serf_recur_handler", serf_recur_handler);
864   return 0;
865 }
866 noit_module_t http = {
867   NOIT_MODULE_MAGIC,
868   NOIT_MODULE_ABI_VERSION,
869   "http",
870   "libserf-based HTTP and HTTPS resource checker",
871   serf_onload,
872   serf_config,
873   serf_init,
874   serf_initiate_check
875 };
876
877 noit_module_t resmon = {
878   NOIT_MODULE_MAGIC,
879   NOIT_MODULE_ABI_VERSION,
880   "resmon",
881   "libserf-based resmon resource checker",
882   serf_onload,
883   resmon_config,
884   serf_init,
885   resmon_initiate_check
886 };
887
888 noit_module_t resmon_part = {
889   NOIT_MODULE_MAGIC,
890   NOIT_MODULE_ABI_VERSION,
891   "resmon_part",
892   "resmon part resource checker",
893   serf_onload,
894   resmon_config,
895   serf_init,
896   resmon_part_initiate_check
897 };
898
Note: See TracBrowser for help on using the browser.