root/src/noit_rest.c

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

switch the check add to PUT, makes more sense. refs #171

  • 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   if(restc->call_closure_free) restc->call_closure_free(restc->call_closure);
159   restc->call_closure_free = NULL;
160   restc->call_closure = NULL;
161   restc->nparams = 0;
162   restc->params = NULL;
163   restc->fastpath = NULL;
164 }
165 static void
166 noit_http_rest_closure_free(noit_http_rest_closure_t *restc) {
167   free(restc->remote_cn);
168   noit_http_rest_clean_request(restc);
169   free(restc);
170 }
171
172 int
173 noit_rest_request_dispatcher(noit_http_session_ctx *ctx) {
174   noit_http_rest_closure_t *restc = ctx->dispatcher_closure;
175   rest_request_handler handler = restc->fastpath;
176   if(!handler) handler = noit_http_get_handler(restc);
177   if(handler) {
178     int rv;
179     rv = handler(restc, restc->nparams, restc->params);
180     if(ctx->res.closed) noit_http_rest_clean_request(restc);
181     return rv;
182   }
183   noit_http_response_status_set(ctx, 404, "NOT FOUND");
184   noit_http_response_option_set(ctx, NOIT_HTTP_CHUNKED);
185   noit_http_rest_clean_request(restc);
186   noit_http_response_end(ctx);
187   return 0;
188 }
189
190 int
191 noit_http_rest_handler(eventer_t e, int mask, void *closure,
192                        struct timeval *now) {
193   int newmask = EVENTER_READ | EVENTER_EXCEPTION;
194   acceptor_closure_t *ac = closure;
195   noit_http_rest_closure_t *restc = ac->service_ctx;
196
197   if(mask & EVENTER_EXCEPTION || (restc && restc->wants_shutdown)) {
198 socket_error:
199     /* Exceptions cause us to simply snip the connection */
200     eventer_remove_fd(e->fd);
201     e->opset->close(e->fd, &newmask, e);
202     if(restc) noit_http_rest_closure_free(restc);
203     if(ac) acceptor_closure_free(ac);
204     return 0;
205   }
206
207   if(!ac->service_ctx) {
208     const char *primer = "";
209     ac->service_ctx = restc = noit_http_rest_closure_alloc();
210     restc->remote_cn = strdup(ac->remote_cn ? ac->remote_cn : "");
211     restc->http_ctx =
212         noit_http_session_ctx_new(noit_rest_request_dispatcher,
213                                   restc, e);
214    
215     switch(ac->cmd) {
216       case NOIT_CONTROL_DELETE:
217         primer = "DELE";
218         break;
219       case NOIT_CONTROL_GET:
220         primer = "GET ";
221         break;
222       case NOIT_CONTROL_HEAD:
223         primer = "HEAD";
224         break;
225       case NOIT_CONTROL_POST:
226         primer = "POST";
227         break;
228       case NOIT_CONTROL_PUT:
229         primer = "PUT ";
230         break;
231       default:
232         goto socket_error;
233     }
234     noit_http_session_prime_input(restc->http_ctx, primer, 4);
235   }
236   return restc->http_ctx->drive(e, mask, restc->http_ctx, now);
237 }
238
239 void noit_http_rest_init() {
240   eventer_name_callback("http_rest_api/1.0", noit_http_rest_handler);
241   noit_control_dispatch_delegate(noit_control_dispatch,
242                                  NOIT_CONTROL_DELETE,
243                                  noit_http_rest_handler);
244   noit_control_dispatch_delegate(noit_control_dispatch,
245                                  NOIT_CONTROL_GET,
246                                  noit_http_rest_handler);
247   noit_control_dispatch_delegate(noit_control_dispatch,
248                                  NOIT_CONTROL_HEAD,
249                                  noit_http_rest_handler);
250   noit_control_dispatch_delegate(noit_control_dispatch,
251                                  NOIT_CONTROL_POST,
252                                  noit_http_rest_handler);
253   noit_control_dispatch_delegate(noit_control_dispatch,
254                                  NOIT_CONTROL_PUT,
255                                  noit_http_rest_handler);
256 }
257
Note: See TracBrowser for help on using the browser.