root/src/modules/http.c

Revision 4f39b4d05385ed2a6a1620e2e3a578b2de6a937a, 32.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

use regular expression instead, and add a body moatcher as a bonus... closes #141

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