root/src/stratcon_realtime_http.c

Revision 8c720f9a4bd5979520e9c7ec00d9bea8d305391b, 19.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

this is a big patch. adds an optional extended-id. you should upgrade your iep first, then your stratcon, then your noits. This is most certainly a flag-day, *but* the feature is off by default... refs #331

  • 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 #include "eventer/eventer.h"
35 #include "noit_conf.h"
36 #include "utils/noit_hash.h"
37 #include "utils/noit_log.h"
38 #include "utils/noit_str.h"
39 #include "jlog/jlog.h"
40 #include "noit_jlog_listener.h"
41 #include "noit_listener.h"
42 #include "noit_http.h"
43 #include "noit_rest.h"
44 #include "noit_check.h"
45 #include "noit_livestream_listener.h"
46 #include "stratcon_realtime_http.h"
47 #include "stratcon_jlog_streamer.h"
48 #include "stratcon_datastore.h"
49
50 #include <ctype.h>
51 #include <unistd.h>
52 #include <assert.h>
53 #include <errno.h>
54 #include <sys/types.h>
55 #include <sys/socket.h>
56 #ifdef HAVE_SYS_FILIO_H
57 #include <sys/filio.h>
58 #endif
59 #include <netinet/in.h>
60 #include <sys/un.h>
61 #include <arpa/inet.h>
62
63
64 typedef struct realtime_recv_ctx_t {
65   int bytes_expected;
66   int bytes_read;
67   int bytes_written;
68   int body_len;
69   char *buffer;         /* These guys are for doing partial reads */
70
71   enum {
72     REALTIME_HTTP_WANT_INITIATE = 0,
73     REALTIME_HTTP_WANT_SEND_INTERVAL = 1,
74     REALTIME_HTTP_WANT_SEND_UUID = 2,
75     REALTIME_HTTP_WANT_HEADER = 3,
76     REALTIME_HTTP_WANT_BODY = 4,
77   } state;
78   int count;            /* Number of jlog messages we need to read */
79   u_int32_t hack_inc_id;
80   noit_http_session_ctx *ctx;
81   struct realtime_tracker *rt;
82 } realtime_recv_ctx_t;
83
84 typedef struct realtime_context {
85   enum { RC_INITIAL = 0, RC_REQ_RECV, RC_INTERESTS_RESOLVED, RC_FEEDING } setup;
86   struct realtime_tracker *checklist;
87   char *document_domain;
88 } realtime_context;
89
90 static realtime_context *alloc_realtime_context(const char *domain) {
91   realtime_context *ctx;
92   ctx = calloc(sizeof(*ctx), 1);
93   ctx->document_domain = strdup(domain);
94   return ctx;
95 }
96 static void free_realtime_tracker(struct realtime_tracker *rt) {
97   if(rt->noit) free(rt->noit);
98   free(rt);
99 }
100 static void clear_realtime_context(realtime_context *rc) {
101  rc->setup = RC_INITIAL;
102   while(rc->checklist) {
103     struct realtime_tracker *tofree;
104     tofree = rc->checklist;
105     rc->checklist = tofree->next;
106     free_realtime_tracker(tofree);
107   }
108   if(rc->document_domain) free(rc->document_domain);
109   rc->document_domain = NULL;
110 }
111 int
112 stratcon_line_to_javascript(noit_http_session_ctx *ctx, char *buff,
113                             u_int32_t inc_id) {
114   char buffer[1024];
115   char *scp, *ecp, *token;
116   int len;
117   void *vcb;
118   const char *v, *cb = NULL;
119   noit_hash_table json = NOIT_HASH_EMPTY;
120   char s_inc_id[42];
121
122   snprintf(s_inc_id, sizeof(s_inc_id), "script-%08x", inc_id);
123   if(noit_hash_retrieve(&ctx->req.querystring, "cb", strlen("cb"), &vcb))
124     cb = vcb;
125   for(v = cb; v && *v; v++)
126     if(!((*v >= '0' && *v <= '9') ||
127          (*v >= 'a' && *v <= 'z') ||
128          (*v >= 'A' && *v <= 'Z') ||
129          (*v == '_') || (*v == '.'))) {
130       cb = NULL;
131       break;
132     }
133   if(!cb) cb = "window.parent.plot_iframe_data";
134
135 #define BAIL_HTTP_WRITE do { \
136   noit_hash_destroy(&json, NULL, free); \
137   noitL(noit_error, "javascript emit failed: %s:%s:%d\n", \
138         __FILE__, __FUNCTION__, __LINE__); \
139   return -1; \
140 } while(0)
141
142 #define PROCESS_NEXT_FIELD(t,l) do { \
143   if(!*scp) goto bad_row; \
144   ecp = strchr(scp, '\t'); \
145   if(!ecp) goto bad_row; \
146   t = scp; \
147   l = (ecp-scp); \
148   scp = ecp + 1; \
149 } while(0)
150 #define PROCESS_LAST_FIELD(t,l) do { \
151   if(!*scp) ecp = scp; \
152   else { \
153     ecp = scp + strlen(scp); /* Puts us at the '\0' */ \
154     if(*(ecp-1) == '\n') ecp--; /* We back up on letter if we ended in \n */ \
155   } \
156   t = scp; \
157   l = (ecp-scp); \
158 } while(0)
159
160   scp = buff;
161   PROCESS_NEXT_FIELD(token,len); /* Skip the leader */
162   if(buff[1] == '\t' && (buff[0] == 'M' || buff[0] == 'S')) {
163     char target[256], module[256], name[256], uuid_str[UUID_STR_LEN+1];
164     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
165     const char *key;
166     int klen, i=0;
167     void *vval;
168     char type[2] = { '\0', '\0' };
169     type[0] = buff[0];
170
171 #define ra_write(a,b) if(noit_http_response_append(ctx, a, b) == noit_false) BAIL_HTTP_WRITE
172
173     snprintf(buffer, sizeof(buffer), "<script id=\"%s\">%s({", s_inc_id, cb);
174     ra_write(buffer, strlen(buffer));
175
176     while(noit_hash_next(&ctx->req.querystring, &iter, &key, &klen, &vval)) {
177       if(!strcmp(key, "cb")) continue;
178       noit_hash_store(&json, key, klen, strdup(vval ?(char *)vval : "true"));
179     }
180     /* Time */
181     noit_hash_store(&json, "script_id", 9, strdup(s_inc_id));
182     noit_hash_store(&json, "type", 4, strdup(type));
183     PROCESS_NEXT_FIELD(token,len);
184     noit_hash_store(&json, "time", 4, noit__strndup(token, len));
185     /* UUID */
186     PROCESS_NEXT_FIELD(token,len);
187     noit_check_extended_id_split(token, len, target, sizeof(target),
188                                  module, sizeof(module), name, sizeof(name),
189                                  uuid_str, sizeof(uuid_str));
190     if(*uuid_str)
191       noit_hash_store(&json, "id", 2,
192                       noit__strndup(uuid_str, strlen(uuid_str)));
193     if(*target)
194       noit_hash_store(&json, "check_target", 12,
195                       noit__strndup(target, strlen(target)));
196     if(*module)
197       noit_hash_store(&json, "check_module", 12,
198                       noit__strndup(module, strlen(module)));
199     if(*name)
200       noit_hash_store(&json, "check_name", 10,
201                       noit__strndup(name, strlen(name)));
202     if(buff[0] == 'M') {
203       /* name */
204       PROCESS_NEXT_FIELD(token,len);
205       noit_hash_store(&json, "metric_name", 11, noit__strndup(token, len));
206       /* type */
207       PROCESS_NEXT_FIELD(token,len);
208       noit_hash_store(&json, "metric_type", 11, noit__strndup(token, len));
209       /* value */
210       PROCESS_LAST_FIELD(token,len); /* value */
211       noit_hash_store(&json, "value", 5, noit__strndup(token, len));
212     }
213     else if(buff[0] == 'S') {
214       /* state */
215       PROCESS_NEXT_FIELD(token,len);
216       noit_hash_store(&json, "check_state", 11, noit__strndup(token, len));
217       /* availability */
218       PROCESS_NEXT_FIELD(token,len);
219       noit_hash_store(&json, "check_availability", 18, noit__strndup(token, len));
220       /* duration */
221       PROCESS_NEXT_FIELD(token,len);
222       noit_hash_store(&json, "check_duration_ms", 17, noit__strndup(token, len));
223       /* status */
224       PROCESS_LAST_FIELD(token,len);
225       noit_hash_store(&json, "status_message", 14, noit__strndup(token, len));
226     }
227
228     memset(&iter, 0, sizeof(iter));
229     while(noit_hash_next(&json, &iter, &key, &klen, &vval)) {
230       char *val = (char *)vval;
231       if(i++) ra_write(",", 1);
232       ra_write("\"", 1);
233       ra_write(key, klen);
234       ra_write("\":\"", 3);
235       while(*val) {
236         if(*val == '\"' || *val == '\\') {
237           ra_write((char *)"\\", 1);
238         }
239         if(isprint(*val)) {
240           ra_write((char *)val, 1);
241         }
242         else {
243           char od[5];
244           snprintf(od, sizeof(od), "\\%03o", *((unsigned char *)val));
245           ra_write(od, strlen(od));
246         }
247         val++;
248       }
249       ra_write("\"", 1);
250     }
251     snprintf(buffer, sizeof(buffer), "});</script>\n");
252     ra_write(buffer, strlen(buffer));
253
254     if(noit_http_response_flush(ctx, noit_false) == noit_false) BAIL_HTTP_WRITE;
255   }
256
257   noit_hash_destroy(&json, NULL, free);
258   return 0;
259
260  bad_row:
261   BAIL_HTTP_WRITE;
262 }
263 int
264 stratcon_realtime_uri_parse(realtime_context *rc, char *uri) {
265   int len, cnt = 0;
266   char *cp, *copy, *interest, *brk;
267   if(strncmp(uri, "/data/", 6)) return 0;
268   cp = uri + 6;
269   len = strlen(cp);
270   copy = alloca(len + 1);
271   if(!copy) return 0;
272   memcpy(copy, cp, len);
273   copy[len] = '\0';
274
275   for (interest = strtok_r(copy, "/", &brk);
276        interest;
277        interest = strtok_r(NULL, "/", &brk)) {
278     uuid_t in_uuid;
279     struct realtime_tracker *node;
280     char *interval;
281
282     interval = strchr(interest, '@');
283     if(!interval)
284       interval = "5000";
285     else
286       *interval++ = '\0';
287     if(uuid_parse(interest, in_uuid)) continue;
288     node = calloc(1, sizeof(*node));
289     node->rc = rc;
290     uuid_copy(node->checkid, in_uuid);
291     node->interval = atoi(interval);
292     node->next = rc->checklist;
293     rc->checklist = node;
294     cnt++;
295   }
296   return cnt;
297 }
298 static void
299 free_realtime_recv_ctx(void *vctx) {
300   realtime_recv_ctx_t *rrctx = vctx;
301   noit_http_session_ctx *ctx = rrctx->ctx;
302   realtime_context *rc = ctx->dispatcher_closure;
303
304   if(noit_atomic_dec32(&ctx->ref_cnt) == 1) {
305     noit_http_response_end(ctx);
306     clear_realtime_context(rc);
307     if(ctx->conn.e) eventer_trigger(ctx->conn.e, EVENTER_WRITE | EVENTER_EXCEPTION);
308   }
309   free(rrctx);
310 }
311 #define Eread(a,b) e->opset->read(e->fd, (a), (b), &mask, e)
312 static int
313 __read_on_ctx(eventer_t e, realtime_recv_ctx_t *ctx, int *newmask) {
314   int len, mask;
315   while(ctx->bytes_read < ctx->bytes_expected) {
316     len = Eread(ctx->buffer + ctx->bytes_read,
317                 ctx->bytes_expected - ctx->bytes_read);
318     if(len < 0) {
319       *newmask = mask;
320       return -1;
321     }
322     /* if we get 0 inside SSL, and there was a real error, we
323      * will actually get a -1 here.
324      * if(len == 0) return ctx->bytes_read;
325      */
326     ctx->bytes_read += len;
327   }
328   assert(ctx->bytes_read == ctx->bytes_expected);
329   return ctx->bytes_read;
330 }
331 #define FULLREAD(e,ctx,size) do { \
332   int mask, len; \
333   if(!ctx->bytes_expected) { \
334     ctx->bytes_expected = size; \
335     if(ctx->buffer) free(ctx->buffer); \
336     ctx->buffer = malloc(size + 1); \
337     if(ctx->buffer == NULL) { \
338       noitL(noit_error, "malloc(%lu) failed.\n", (unsigned long)size + 1); \
339       goto socket_error; \
340     } \
341     ctx->buffer[size] = '\0'; \
342   } \
343   len = __read_on_ctx(e, ctx, &mask); \
344   if(len < 0) { \
345     if(errno == EAGAIN) return mask | EVENTER_EXCEPTION; \
346     noitL(noit_error, "SSL read error: %s\n", strerror(errno)); \
347     goto socket_error; \
348   } \
349   ctx->bytes_read = 0; \
350   ctx->bytes_expected = 0; \
351   if(len != size) { \
352     noitL(noit_error, "SSL short read [%d] (%d/%lu).  Reseting connection.\n", \
353           ctx->state, len, (unsigned long)size); \
354     goto socket_error; \
355   } \
356 } while(0)
357
358 int
359 stratcon_realtime_recv_handler(eventer_t e, int mask, void *closure,
360                                struct timeval *now) {
361   static u_int32_t livestream_cmd = 0;
362   noit_connection_ctx_t *nctx = closure;
363   realtime_recv_ctx_t *ctx = nctx->consumer_ctx;
364   int len;
365   u_int32_t nint;
366   char uuid_str[37];
367
368   if(!livestream_cmd) livestream_cmd = htonl(NOIT_LIVESTREAM_DATA_FEED);
369
370   if(mask & EVENTER_EXCEPTION || nctx->wants_shutdown) {
371  socket_error:
372     ctx->state = REALTIME_HTTP_WANT_INITIATE;
373     ctx->count = 0;
374     ctx->bytes_read = 0;
375     ctx->bytes_written = 0;
376     ctx->bytes_expected = 0;
377     if(ctx->buffer) free(ctx->buffer);
378     ctx->buffer = NULL;
379     noit_connection_ctx_dealloc(nctx);
380     eventer_remove_fd(e->fd);
381     e->opset->close(e->fd, &mask, e);
382     return 0;
383   }
384
385 #define full_nb_write(data, wlen) do { \
386   if(!ctx->bytes_expected) { \
387     ctx->bytes_written = 0; \
388     ctx->bytes_expected = wlen; \
389   } \
390   while(ctx->bytes_written < ctx->bytes_expected) { \
391     while(-1 == (len = e->opset->write(e->fd, ((char *)data) + ctx->bytes_written, \
392                                        ctx->bytes_expected - ctx->bytes_written, \
393                                        &mask, e)) && errno == EINTR); \
394     if(len < 0) { \
395       if(errno == EAGAIN) return mask | EVENTER_EXCEPTION; \
396       goto socket_error; \
397     } \
398     ctx->bytes_written += len; \
399   } \
400   if(ctx->bytes_written != ctx->bytes_expected) { \
401     noitL(noit_error, "short write on initiating stream [%d != %d].\n", \
402           ctx->bytes_written, ctx->bytes_expected); \
403     goto socket_error; \
404   } \
405   ctx->bytes_expected = 0; \
406 } while(0)
407
408   noit_connection_update_timeout(nctx);
409   while(1) {
410     u_int32_t net_body_len;
411
412     switch(ctx->state) {
413       case REALTIME_HTTP_WANT_INITIATE:
414         full_nb_write(&livestream_cmd, sizeof(livestream_cmd));
415         ctx->state = REALTIME_HTTP_WANT_SEND_INTERVAL;
416       case REALTIME_HTTP_WANT_SEND_INTERVAL:
417         nint = htonl(ctx->rt->interval);
418         full_nb_write(&nint, sizeof(nint));
419         ctx->state = REALTIME_HTTP_WANT_SEND_UUID;
420       case REALTIME_HTTP_WANT_SEND_UUID:
421         uuid_unparse_lower(ctx->rt->checkid, uuid_str);
422         full_nb_write(uuid_str, 36);
423         ctx->state = REALTIME_HTTP_WANT_HEADER;
424       case REALTIME_HTTP_WANT_HEADER:
425         FULLREAD(e, ctx, sizeof(u_int32_t));
426         memcpy(&net_body_len, ctx->buffer, sizeof(u_int32_t));
427         ctx->body_len = ntohl(net_body_len);
428         free(ctx->buffer); ctx->buffer = NULL;
429         ctx->state = REALTIME_HTTP_WANT_BODY;
430         break;
431       case REALTIME_HTTP_WANT_BODY:
432         FULLREAD(e, ctx, ctx->body_len);
433         if(stratcon_line_to_javascript(ctx->ctx, ctx->buffer, ctx->hack_inc_id++)) goto socket_error;
434         free(ctx->buffer); ctx->buffer = NULL;
435         ctx->state = REALTIME_HTTP_WANT_HEADER;
436         break;
437     }
438   }
439
440 }
441
442 int
443 stratcon_realtime_http_postresolve(eventer_t e, int mask, void *closure,
444                                    struct timeval *now) {
445   noit_http_session_ctx *ctx = closure;
446   realtime_context *rc = ctx->dispatcher_closure;
447   struct realtime_tracker *node;
448
449   for(node = rc->checklist; node; node = node->next) {
450     if(node->noit) {
451       realtime_recv_ctx_t *rrctx;
452       rrctx = calloc(1, sizeof(*rrctx));
453       rrctx->ctx = ctx;
454       rrctx->rt = node;
455       stratcon_streamer_connection(NULL, node->noit,
456                                    stratcon_realtime_recv_handler,
457                                    NULL, rrctx,
458                                    free_realtime_recv_ctx);
459     }
460     else
461       noit_atomic_dec32(&ctx->ref_cnt);
462   }
463   if(ctx->ref_cnt == 1) {
464     noit_http_response_end(ctx);
465     clear_realtime_context(rc);
466     if(ctx->conn.e) eventer_trigger(ctx->conn.e, EVENTER_WRITE);
467   }
468   return 0;
469 }
470 int
471 stratcon_request_dispatcher(noit_http_session_ctx *ctx) {
472   const char *key, *value;
473   realtime_context *rc = ctx->dispatcher_closure;
474   int klen;
475   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
476   noit_http_request *req = &ctx->req;
477
478   if(rc->setup == RC_INITIAL) {
479     eventer_t completion;
480     struct realtime_tracker *node;
481     char c[1024];
482     int num_interests;
483
484     num_interests = stratcon_realtime_uri_parse(rc, ctx->req.uri_str);
485     if(num_interests == 0) {
486       noit_http_response_status_set(ctx, 404, "OK");
487       noit_http_response_option_set(ctx, NOIT_HTTP_CLOSE);
488       noit_http_response_end(ctx);
489       return 0;
490     }
491
492     noitL(noit_error, "http: %s %s %s\n",
493           req->method_str, req->uri_str, req->protocol_str);
494     while(noit_hash_next_str(&req->headers, &iter, &key, &klen, &value)) {
495       noitL(noit_error, "http: [%s: %s]\n", key, value);
496     }
497     noit_http_response_status_set(ctx, 200, "OK");
498     noit_http_response_option_set(ctx, NOIT_HTTP_CHUNKED);
499     /*noit_http_response_option_set(ctx, NOIT_HTTP_GZIP);*/
500     /*noit_http_response_option_set(ctx, NOIT_HTTP_DEFLATE);*/
501     noit_http_response_header_set(ctx, "Content-Type", "text/html");
502
503     snprintf(c, sizeof(c),
504              "<html><head><script>document.domain='%s';</script></head><body>\n",
505              rc->document_domain);
506     noit_http_response_append(ctx, c, strlen(c));
507
508     /* this dumb crap is to make some browsers happy (Safari) */
509     memset(c, ' ', sizeof(c));
510     noit_http_response_append(ctx, c, sizeof(c));
511     noit_http_response_flush(ctx, noit_false);
512
513     rc->setup = RC_REQ_RECV;
514     /* Each interest references the ctx */
515     for(node = rc->checklist; node; node = node->next) {
516       char uuid_str[UUID_STR_LEN+1];
517       noit_atomic_inc32(&ctx->ref_cnt);
518       uuid_unparse_lower(node->checkid, uuid_str);
519       noitL(noit_error, "Resolving uuid: %s\n", uuid_str);
520     }
521     completion = eventer_alloc();
522     completion->mask = EVENTER_TIMER;
523     completion->callback = stratcon_realtime_http_postresolve;
524     completion->closure = ctx;
525     gettimeofday(&completion->whence, NULL);
526     stratcon_datastore_push(DS_OP_FIND_COMPLETE, NULL, NULL,
527                             rc->checklist, completion);
528   }
529   return EVENTER_EXCEPTION;
530 }
531 int
532 stratcon_realtime_http_handler(eventer_t e, int mask, void *closure,
533                                struct timeval *now) {
534   int done = 0, rv;
535   acceptor_closure_t *ac = closure;
536   noit_http_session_ctx *http_ctx = ac->service_ctx;
537   rv = noit_http_session_drive(e, mask, http_ctx, now, &done);
538   if(done) acceptor_closure_free(ac);
539   return rv;
540 }
541 static int
542 rest_stream_data(noit_http_rest_closure_t *restc,
543                  int npats, char **pats) {
544   /* We're here and want to subvert the rest system */
545   const char *document_domain = NULL;
546   noit_http_session_ctx *ctx = restc->http_ctx;
547   acceptor_closure_t *ac = restc->ac;
548
549   /* Rewire the handler */
550   if(ac->service_ctx_free)
551     ac->service_ctx_free(ac->service_ctx);
552   ac->service_ctx = ctx;
553   ac->service_ctx_free = noit_http_ctx_acceptor_free;
554
555   if(!noit_hash_retr_str(ac->config,
556                          "document_domain", strlen("document_domain"),
557                          &document_domain)) {
558     noitL(noit_error, "Document domain not set!  Realtime streaming will be broken\n");
559     document_domain = "";
560   }
561
562   noit_http_process_querystring(&ctx->req);
563   /* Rewire the http context */
564   ctx->conn.e->callback = stratcon_realtime_http_handler;
565   ctx->dispatcher = stratcon_request_dispatcher;
566   ctx->dispatcher_closure = alloc_realtime_context(document_domain);
567   return stratcon_request_dispatcher(ctx);
568 }
569
570 void
571 stratcon_realtime_http_init(const char *toplevel) {
572   eventer_name_callback("stratcon_realtime_http",
573                         stratcon_realtime_http_handler);
574   eventer_name_callback("stratcon_realtime_recv",
575                         stratcon_realtime_recv_handler);
576   assert(noit_http_rest_register_auth(
577     "GET", "/data/",
578            "^((?:" UUID_REGEX "(?:@\\d+)?)(?:/" UUID_REGEX "(?:@\\d+)?)*)$",
579     rest_stream_data, noit_http_rest_access
580   ) == 0);
581   assert(noit_http_rest_register_auth(
582     "GET", "/", "^(.*)$", noit_rest_simple_file_handler, noit_http_rest_access
583   ) == 0);
584 }
Note: See TracBrowser for help on using the browser.