root/src/noit_rest.c

Revision 3c56016c44ec467d9baa6ed0d667e21bf44f81cb, 11.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

insert the new rest API as a shim between the http listener and the realtime streamer -- for future extensibility

  • 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 #include <errno.h>
40
41 struct rest_xml_payload {
42   char *buffer;
43   xmlDocPtr indoc;
44   int len;
45   int allocd;
46   int complete;
47 };
48
49 struct rest_url_dispatcher {
50   char *method;
51   pcre *expression;
52   pcre_extra *extra;
53   rest_request_handler handler;
54   /* Chain to the next one */
55   struct rest_url_dispatcher *next;
56 };
57
58 struct rule_container {
59   char *base;
60   struct rest_url_dispatcher *rules;
61   struct rest_url_dispatcher *rules_endptr;
62 };
63 noit_hash_table dispatch_points = NOIT_HASH_EMPTY;
64
65 static rest_request_handler
66 noit_http_get_handler(noit_http_rest_closure_t *restc) {
67   struct rule_container *cont = NULL;
68   struct rest_url_dispatcher *rule;
69   char *eoq, *eob;
70   eoq = strchr(restc->http_ctx->req.uri_str, '?');
71   if(!eoq)
72     eoq = restc->http_ctx->req.uri_str + strlen(restc->http_ctx->req.uri_str);
73   eob = eoq - 1;
74
75   /* find the right base */
76   while(1) {
77     void *vcont;
78     while(eob >= restc->http_ctx->req.uri_str && *eob != '/') eob--;
79     if(eob < restc->http_ctx->req.uri_str) break; /* off the front */
80     if(noit_hash_retrieve(&dispatch_points, restc->http_ctx->req.uri_str,
81                           eob - restc->http_ctx->req.uri_str + 1, &vcont)) {
82       cont = vcont;
83       eob++; /* move past the determined base */
84       break;
85     }
86     eob--;
87   }
88   if(!cont) return NULL;
89   for(rule = cont->rules; rule; rule = rule->next) {
90     int ovector[30];
91     int cnt;
92     if(strcmp(rule->method, restc->http_ctx->req.method_str)) continue;
93     if((cnt = pcre_exec(rule->expression, rule->extra, eob, eoq - eob, 0, 0,
94                         ovector, sizeof(ovector)/sizeof(*ovector))) > 0) {
95       /* We match, set 'er up */
96       restc->fastpath = rule->handler;
97       restc->nparams = cnt - 1;
98       if(restc->nparams) {
99         restc->params = calloc(restc->nparams, sizeof(*restc->params));
100         for(cnt = 0; cnt < restc->nparams; cnt++) {
101           int start = ovector[(cnt+1)*2];
102           int end = ovector[(cnt+1)*2+1];
103           restc->params[cnt] = malloc(end - start + 1);
104           memcpy(restc->params[cnt], eob + start, end - start);
105           restc->params[cnt][end - start] = '\0';
106         }
107       }
108       return restc->fastpath;
109     }
110   }
111   return NULL;
112 }
113 int
114 noit_http_rest_register(const char *method, const char *base,
115                         const char *expr, rest_request_handler f) {
116   void *vcont;
117   struct rule_container *cont;
118   struct rest_url_dispatcher *rule;
119   const char *error;
120   int erroffset;
121   pcre *pcre_expr;
122   int blen = strlen(base);
123   /* base must end in a /, 'cause I said so */
124   if(blen == 0 || base[blen-1] != '/') return -1;
125   pcre_expr = pcre_compile(expr, 0, &error, &erroffset, NULL);
126   if(!pcre_expr) {
127     noitL(noit_error, "Error in rest expr(%s) '%s'@%d: %s\n",
128           base, expr, erroffset, error);
129     return -1;
130   }
131   rule = calloc(1, sizeof(*rule));
132   rule->method = strdup(method);
133   rule->expression = pcre_expr;
134   rule->extra = pcre_study(rule->expression, 0, &error);
135   rule->handler = f;
136
137   /* Make sure we have a container */
138   if(!noit_hash_retrieve(&dispatch_points, base, strlen(base), &vcont)) {
139     cont = calloc(1, sizeof(*cont));
140     cont->base = strdup(base);
141     noit_hash_store(&dispatch_points, cont->base, strlen(cont->base), cont);
142   }
143   else cont = vcont;
144
145   /* Append the rule */
146   if(cont->rules_endptr) {
147     cont->rules_endptr->next = rule;
148     cont->rules_endptr = cont->rules_endptr->next;
149   }
150   else
151     cont->rules = cont->rules_endptr = rule;
152   return 0;
153 }
154
155 static noit_http_rest_closure_t *
156 noit_http_rest_closure_alloc() {
157   noit_http_rest_closure_t *restc;
158   restc = calloc(1, sizeof(*restc));
159   return restc;
160 }
161 static void
162 noit_http_rest_clean_request(noit_http_rest_closure_t *restc) {
163   int i;
164   if(restc->nparams) {
165     for(i=0;i<restc->nparams;i++) free(restc->params[i]);
166     free(restc->params);
167   }
168   if(restc->call_closure_free) restc->call_closure_free(restc->call_closure);
169   restc->call_closure_free = NULL;
170   restc->call_closure = NULL;
171   restc->nparams = 0;
172   restc->params = NULL;
173   restc->fastpath = NULL;
174 }
175 void
176 noit_http_rest_closure_free(noit_http_rest_closure_t *restc) {
177   free(restc->remote_cn);
178   noit_http_rest_clean_request(restc);
179   free(restc);
180 }
181
182 int
183 noit_rest_request_dispatcher(noit_http_session_ctx *ctx) {
184   noit_http_rest_closure_t *restc = ctx->dispatcher_closure;
185   rest_request_handler handler = restc->fastpath;
186   if(!handler) handler = noit_http_get_handler(restc);
187   if(handler) {
188     int rv;
189     rv = handler(restc, restc->nparams, restc->params);
190     if(ctx->res.closed) noit_http_rest_clean_request(restc);
191     return rv;
192   }
193   noit_http_response_status_set(ctx, 404, "NOT FOUND");
194   noit_http_response_option_set(ctx, NOIT_HTTP_CHUNKED);
195   noit_http_rest_clean_request(restc);
196   noit_http_response_end(ctx);
197   return 0;
198 }
199
200 int
201 noit_http_rest_handler(eventer_t e, int mask, void *closure,
202                        struct timeval *now) {
203   int newmask = EVENTER_READ | EVENTER_EXCEPTION;
204   acceptor_closure_t *ac = closure;
205   noit_http_rest_closure_t *restc = ac->service_ctx;
206
207   if(mask & EVENTER_EXCEPTION || (restc && restc->wants_shutdown)) {
208 socket_error:
209     /* Exceptions cause us to simply snip the connection */
210     eventer_remove_fd(e->fd);
211     e->opset->close(e->fd, &newmask, e);
212     if(restc) noit_http_rest_closure_free(restc);
213     if(ac) acceptor_closure_free(ac);
214     return 0;
215   }
216
217   if(!ac->service_ctx) {
218     const char *primer = "";
219     ac->service_ctx = restc = noit_http_rest_closure_alloc();
220     restc->ac = ac;
221     restc->remote_cn = strdup(ac->remote_cn ? ac->remote_cn : "");
222     restc->http_ctx =
223         noit_http_session_ctx_new(noit_rest_request_dispatcher,
224                                   restc, e);
225    
226     switch(ac->cmd) {
227       case NOIT_CONTROL_DELETE:
228         primer = "DELE";
229         break;
230       case NOIT_CONTROL_GET:
231         primer = "GET ";
232         break;
233       case NOIT_CONTROL_HEAD:
234         primer = "HEAD";
235         break;
236       case NOIT_CONTROL_POST:
237         primer = "POST";
238         break;
239       case NOIT_CONTROL_PUT:
240         primer = "PUT ";
241         break;
242       default:
243         goto socket_error;
244     }
245     noit_http_session_prime_input(restc->http_ctx, primer, 4);
246   }
247   return restc->http_ctx->drive(e, mask, restc->http_ctx, now);
248 }
249
250 int
251 noit_http_rest_raw_handler(eventer_t e, int mask, void *closure,
252                            struct timeval *now) {
253   int newmask = EVENTER_READ | EVENTER_EXCEPTION;
254   acceptor_closure_t *ac = closure;
255   noit_http_rest_closure_t *restc = ac->service_ctx;
256
257   if(mask & EVENTER_EXCEPTION || (restc && restc->wants_shutdown)) {
258     /* Exceptions cause us to simply snip the connection */
259     eventer_remove_fd(e->fd);
260     e->opset->close(e->fd, &newmask, e);
261     if(restc) noit_http_rest_closure_free(restc);
262     if(ac) acceptor_closure_free(ac);
263     return 0;
264   }
265   if(!ac->service_ctx) {
266     ac->service_ctx = restc = noit_http_rest_closure_alloc();
267     restc->ac = ac;
268     restc->http_ctx =
269         noit_http_session_ctx_new(noit_rest_request_dispatcher,
270                                   restc, e);
271   }
272   return restc->http_ctx->drive(e, mask, restc->http_ctx, now);
273 }
274
275 static void
276 rest_xml_payload_free(void *f) {
277   struct rest_xml_payload *xmlin = f;
278   if(xmlin->buffer) free(xmlin->buffer);
279   if(xmlin->indoc) xmlFreeDoc(xmlin->indoc);
280 }
281
282 xmlDocPtr
283 rest_get_xml_upload(noit_http_rest_closure_t *restc,
284                     int *mask, int *complete) {
285   struct rest_xml_payload *rxc;
286   if(restc->call_closure == NULL) {
287     rxc = restc->call_closure = calloc(1, sizeof(*rxc));
288     restc->call_closure_free = rest_xml_payload_free;
289   }
290   rxc = restc->call_closure;
291   while(!rxc->complete) {
292     int len;
293     if(rxc->len == rxc->allocd) {
294       char *b;
295       rxc->allocd += 32768;
296       b = rxc->buffer ? realloc(rxc->buffer, rxc->allocd) :
297                         malloc(rxc->allocd);
298       if(!b) {
299         *complete = 1;
300         return NULL;
301       }
302       rxc->buffer = b;
303     }
304     len = noit_http_session_req_consume(restc->http_ctx,
305                                         rxc->buffer + rxc->len,
306                                         rxc->allocd - rxc->len,
307                                         mask);
308     if(len > 0) rxc->len += len;
309     if(len < 0 && errno == EAGAIN) return NULL;
310     else if(len < 0) {
311       *complete = 1;
312       return NULL;
313     }
314     if(rxc->len == restc->http_ctx->req.content_length) {
315       rxc->indoc = xmlParseMemory(rxc->buffer, rxc->len);
316       rxc->complete = 1;
317     }
318   }
319
320   *complete = 1;
321   return rxc->indoc;
322 }
323 void noit_http_rest_init() {
324   eventer_name_callback("noit_wite_rest_api/1.0", noit_http_rest_handler);
325   eventer_name_callback("http_rest_api", noit_http_rest_raw_handler);
326   noit_control_dispatch_delegate(noit_control_dispatch,
327                                  NOIT_CONTROL_DELETE,
328                                  noit_http_rest_handler);
329   noit_control_dispatch_delegate(noit_control_dispatch,
330                                  NOIT_CONTROL_GET,
331                                  noit_http_rest_handler);
332   noit_control_dispatch_delegate(noit_control_dispatch,
333                                  NOIT_CONTROL_HEAD,
334                                  noit_http_rest_handler);
335   noit_control_dispatch_delegate(noit_control_dispatch,
336                                  NOIT_CONTROL_POST,
337                                  noit_http_rest_handler);
338   noit_control_dispatch_delegate(noit_control_dispatch,
339                                  NOIT_CONTROL_PUT,
340                                  noit_http_rest_handler);
341 }
342
Note: See TracBrowser for help on using the browser.