root/src/noit_rest.c

Revision 02569455fdf31dd93db18514b0b4cda5d5181ff7, 8.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

work on #171, getting check info works

  • 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 "noit_listener.h"
35 #include "noit_http.h"
36 #include "noit_rest.h"
37
38 #include <pcre.h>
39
40 struct rest_url_dispatcher {
41   char *method;
42   pcre *expression;
43   pcre_extra *extra;
44   rest_request_handler handler;
45   /* Chain to the next one */
46   struct rest_url_dispatcher *next;
47 };
48 struct rule_container {
49   char *base;
50   struct rest_url_dispatcher *rules;
51   struct rest_url_dispatcher *rules_endptr;
52 };
53 noit_hash_table dispatch_points = NOIT_HASH_EMPTY;
54
55 static rest_request_handler
56 noit_http_get_handler(noit_http_rest_closure_t *restc) {
57   struct rule_container *cont = NULL;
58   struct rest_url_dispatcher *rule;
59   char *eoq, *eob;
60   eoq = strchr(restc->http_ctx->req.uri_str, '?');
61   if(!eoq)
62     eoq = restc->http_ctx->req.uri_str + strlen(restc->http_ctx->req.uri_str);
63   eob = eoq - 1;
64
65   /* find the right base */
66   while(1) {
67     void *vcont;
68     while(eob >= restc->http_ctx->req.uri_str && *eob != '/') eob--;
69     if(eob < restc->http_ctx->req.uri_str) break; /* off the front */
70     if(noit_hash_retrieve(&dispatch_points, restc->http_ctx->req.uri_str,
71                           eob - restc->http_ctx->req.uri_str + 1, &vcont)) {
72       cont = vcont;
73       eob++; /* move past the determined base */
74       break;
75     }
76     eob--;
77   }
78   if(!cont) return NULL;
79   for(rule = cont->rules; rule; rule = rule->next) {
80     int ovector[30];
81     int cnt;
82     if(strcmp(rule->method, restc->http_ctx->req.method_str)) continue;
83     if((cnt = pcre_exec(rule->expression, rule->extra, eob, eoq - eob, 0, 0,
84                         ovector, sizeof(ovector)/sizeof(*ovector))) > 0) {
85       /* We match, set 'er up */
86       restc->fastpath = rule->handler;
87       restc->nparams = cnt - 1;
88       if(restc->nparams) {
89         restc->params = calloc(restc->nparams, sizeof(*restc->params));
90         for(cnt = 0; cnt < restc->nparams; cnt++) {
91           int start = ovector[(cnt+1)*2];
92           int end = ovector[(cnt+1)*2+1];
93           restc->params[cnt] = malloc(end - start + 1);
94           memcpy(restc->params[cnt], eob + start, end - start);
95           restc->params[cnt][end - start] = '\0';
96         }
97       }
98       return restc->fastpath;
99     }
100   }
101   return NULL;
102 }
103 int
104 noit_http_rest_register(const char *method, const char *base,
105                         const char *expr, rest_request_handler f) {
106   void *vcont;
107   struct rule_container *cont;
108   struct rest_url_dispatcher *rule;
109   const char *error;
110   int erroffset;
111   pcre *pcre_expr;
112   int blen = strlen(base);
113   /* base must end in a /, 'cause I said so */
114   if(blen == 0 || base[blen-1] != '/') return -1;
115   pcre_expr = pcre_compile(expr, 0, &error, &erroffset, NULL);
116   if(!pcre_expr) {
117     noitL(noit_error, "Error in rest expr(%s) '%s'@%d: %s\n",
118           base, expr, erroffset, error);
119     return -1;
120   }
121   rule = calloc(1, sizeof(*rule));
122   rule->method = strdup(method);
123   rule->expression = pcre_expr;
124   rule->extra = pcre_study(rule->expression, 0, &error);
125   rule->handler = f;
126
127   /* Make sure we have a container */
128   if(!noit_hash_retrieve(&dispatch_points, base, strlen(base), &vcont)) {
129     cont = calloc(1, sizeof(*cont));
130     cont->base = strdup(base);
131     noit_hash_store(&dispatch_points, cont->base, strlen(cont->base), cont);
132   }
133   else cont = vcont;
134
135   /* Append the rule */
136   if(cont->rules_endptr) {
137     cont->rules_endptr->next = rule;
138     cont->rules_endptr = cont->rules_endptr->next;
139   }
140   else
141     cont->rules = cont->rules_endptr = rule;
142   return 0;
143 }
144
145 static noit_http_rest_closure_t *
146 noit_http_rest_closure_alloc() {
147   noit_http_rest_closure_t *restc;
148   restc = calloc(1, sizeof(*restc));
149   return restc;
150 }
151 static void
152 noit_http_rest_clean_request(noit_http_rest_closure_t *restc) {
153   int i;
154   if(restc->nparams) {
155     for(i=0;i<restc->nparams;i++) free(restc->params[i]);
156     free(restc->params);
157   }
158   restc->nparams = 0;
159   restc->params = NULL;
160   restc->fastpath = NULL;
161 }
162 static void
163 noit_http_rest_closure_free(noit_http_rest_closure_t *restc) {
164   free(restc->remote_cn);
165    noit_http_rest_clean_request(restc);
166   free(restc);
167 }
168
169 int
170 noit_rest_request_dispatcher(noit_http_session_ctx *ctx) {
171   noit_http_rest_closure_t *restc = ctx->dispatcher_closure;
172   rest_request_handler handler = restc->fastpath;
173   if(!handler) handler = noit_http_get_handler(restc);
174   if(handler) {
175     int rv;
176     rv = handler(restc, restc->nparams, restc->params);
177     if(ctx->res.closed) noit_http_rest_clean_request(restc);
178     return rv;
179   }
180   noit_http_response_status_set(ctx, 404, "NOT FOUND");
181   noit_http_response_option_set(ctx, NOIT_HTTP_CHUNKED);
182   noit_http_rest_clean_request(restc);
183   noit_http_response_end(ctx);
184   return 0;
185 }
186
187 int
188 noit_http_rest_handler(eventer_t e, int mask, void *closure,
189                        struct timeval *now) {
190   int newmask = EVENTER_READ | EVENTER_EXCEPTION;
191   acceptor_closure_t *ac = closure;
192   noit_http_rest_closure_t *restc = ac->service_ctx;
193
194   if(mask & EVENTER_EXCEPTION || (restc && restc->wants_shutdown)) {
195 socket_error:
196     /* Exceptions cause us to simply snip the connection */
197     eventer_remove_fd(e->fd);
198     e->opset->close(e->fd, &newmask, e);
199     if(restc) noit_http_rest_closure_free(restc);
200     if(ac) acceptor_closure_free(ac);
201     return 0;
202   }
203
204   if(!ac->service_ctx) {
205     const char *primer = "";
206     ac->service_ctx = restc = noit_http_rest_closure_alloc();
207     restc->remote_cn = strdup(ac->remote_cn ? ac->remote_cn : "");
208     restc->http_ctx =
209         noit_http_session_ctx_new(noit_rest_request_dispatcher,
210                                   restc, e);
211    
212     switch(ac->cmd) {
213       case NOIT_CONTROL_POST:
214         primer = "POST";
215         break;
216       case NOIT_CONTROL_GET:
217         primer = "GET ";
218         break;
219       case NOIT_CONTROL_HEAD:
220         primer = "HEAD";
221         break;
222       default:
223         goto socket_error;
224     }
225     noit_http_session_prime_input(restc->http_ctx, primer, 4);
226   }
227   return restc->http_ctx->drive(e, mask, restc->http_ctx, now);
228 }
229
230 void noit_http_rest_init() {
231   eventer_name_callback("http_rest_api/1.0", noit_http_rest_handler);
232   noit_control_dispatch_delegate(noit_control_dispatch,
233                                  NOIT_CONTROL_GET,
234                                  noit_http_rest_handler);
235   noit_control_dispatch_delegate(noit_control_dispatch,
236                                  NOIT_CONTROL_HEAD,
237                                  noit_http_rest_handler);
238   noit_control_dispatch_delegate(noit_control_dispatch,
239                                  NOIT_CONTROL_POST,
240                                  noit_http_rest_handler);
241 }
242
Note: See TracBrowser for help on using the browser.