root/src/noit_rest.c

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

closes #196

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