1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2011 Olaf Wintermann. All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
26 * POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 #include <stdio.h> |
|
30 #include <stdlib.h> |
|
31 |
|
32 |
|
33 #include "nsapi.h" |
|
34 #include "pool.h" |
|
35 #include "pblock.h" |
|
36 #include "io.h" |
|
37 #include "util.h" |
|
38 #include "httprequest.h" |
|
39 #include "conf.h" |
|
40 #include "vserver.h" |
|
41 |
|
42 HTTPRequest *http_request_new() { |
|
43 HTTPRequest *req = malloc(sizeof(HTTPRequest)); |
|
44 req->connection = NULL; |
|
45 req->uri.ptr = NULL; |
|
46 |
|
47 HeaderArray *hd = malloc(sizeof(HeaderArray)); |
|
48 hd->next = NULL; |
|
49 hd->len = 0; |
|
50 hd->headers = calloc(16, sizeof(Header)); |
|
51 hd->alloc = 16; |
|
52 |
|
53 req->headers = hd; |
|
54 |
|
55 return req; |
|
56 } |
|
57 |
|
58 int handle_request(HTTPRequest *request) { |
|
59 // handle nsapi request |
|
60 |
|
61 // create pool |
|
62 request->pool = pool_create(); |
|
63 |
|
64 // create nsapi data structures |
|
65 NSAPISession *sn = malloc(sizeof(NSAPISession)); |
|
66 NSAPIRequest *rq = malloc(sizeof(NSAPIRequest)); |
|
67 request->rq = rq; |
|
68 rq->phase = NSAPIAuthTrans; |
|
69 |
|
70 // fill session structure |
|
71 sn->sys_fd = request->connection->fd; |
|
72 sn->sn.pool = pool_create(); |
|
73 sn->sn.csd = stream_new_from_fd(request->connection->fd); |
|
74 sn->sn.client = pblock_create_pool(sn->sn.pool, 8); |
|
75 sn->sn.next = NULL; |
|
76 sn->sn.fill = 1; |
|
77 sn->sn.subject = NULL; |
|
78 |
|
79 /* add ip to sn->client pblock */ |
|
80 char ip_str[INET_ADDRSTRLEN]; |
|
81 if(inet_ntop( |
|
82 AF_INET, |
|
83 &request->connection->address.sin_addr, |
|
84 ip_str, |
|
85 INET_ADDRSTRLEN) != NULL) |
|
86 { |
|
87 pblock_kvinsert(pb_key_ip, ip_str, INET_ADDRSTRLEN, sn->sn.client); |
|
88 } |
|
89 |
|
90 // init NSAPI request structure |
|
91 if(request_initialize(request->pool, request, rq) != 0) { |
|
92 printf("Cannot initialize request structure\n"); |
|
93 return 1; |
|
94 } |
|
95 |
|
96 // set default virtual server |
|
97 rq->vs = conf_get_default_vs(); |
|
98 |
|
99 |
|
100 /* Pass request line as "clf-request" */ |
|
101 pblock_kvinsert( |
|
102 pb_key_clf_request, |
|
103 request->request_line.ptr, |
|
104 request->request_line.length, |
|
105 rq->rq.reqpb); |
|
106 |
|
107 /* Pass method as "method" in reqpb, and also as method_num */ |
|
108 pblock_kvinsert( |
|
109 pb_key_method, |
|
110 request->method.ptr, |
|
111 request->method.length, |
|
112 rq->rq.reqpb); |
|
113 // TODO: method num |
|
114 //rqRq.rq.method_num = rqHdr->GetMethodNumber(); |
|
115 //PR_ASSERT(rqRq.rq.method_num != -1 || iStatus); |
|
116 |
|
117 /* Pass protocol as "protocol" in reqpb, and also in protv_num */ |
|
118 pblock_kvinsert( |
|
119 pb_key_protocol, |
|
120 request->httpv.ptr, |
|
121 request->httpv.length, |
|
122 rq->rq.reqpb); |
|
123 // TODO: protocol num |
|
124 |
|
125 /* Pass any query as "query" in reqpb */ |
|
126 // TODO: query |
|
127 |
|
128 /* Get abs_path part of request URI, and canonicalize the path */ |
|
129 sstr_t absPath = request->uri; |
|
130 // TODO: get abs_path |
|
131 absPath.ptr = util_canonicalize_uri( |
|
132 request->pool, |
|
133 absPath.ptr, |
|
134 absPath.length, |
|
135 (int*)&absPath.length); |
|
136 |
|
137 /* Decode the abs_path */ |
|
138 // TODO: decode abs_path |
|
139 |
|
140 /* Pass the abs_path as "uri" in reqpb */ |
|
141 // TODO: pass abs_path to reqpb |
|
142 // TODO: replace this code |
|
143 pblock_kvinsert( |
|
144 pb_key_uri, |
|
145 absPath.ptr, |
|
146 absPath.length, |
|
147 rq->rq.reqpb); |
|
148 |
|
149 // pass http header to the NSAPI request structure |
|
150 int hlen = request->headers->len; |
|
151 HeaderArray *ha = request->headers; |
|
152 for(int i=0;i<=hlen;i++) { |
|
153 if(i == hlen) { |
|
154 ha = ha->next; |
|
155 if(ha == NULL) { |
|
156 break; |
|
157 } |
|
158 i = 0; |
|
159 hlen = ha->len; |
|
160 } |
|
161 |
|
162 if(ha->headers[i].name[0] < 90) { |
|
163 ha->headers[i].name[0] += 32; |
|
164 } |
|
165 pblock_nvinsert(ha->headers[i].name, ha->headers[i].value, rq->rq.headers); |
|
166 } |
|
167 |
|
168 /* check for request body and prepare input buffer */ |
|
169 char *ctlen_str = pblock_findkeyval(pb_key_content_length, rq->rq.headers); |
|
170 if(ctlen_str) { |
|
171 int ctlen = atoi(ctlen_str); |
|
172 |
|
173 printf("request body length: %d\n", ctlen); |
|
174 |
|
175 netbuf *nb = request->netbuf; |
|
176 |
|
177 /* create new netbuf */ |
|
178 NetIOStream *net_io = (NetIOStream*)net_stream_from_fd( |
|
179 request->connection->fd); |
|
180 net_io->max_read = ctlen; |
|
181 |
|
182 sn->sn.inbuf = malloc(sizeof(netbuf)); |
|
183 sn->sn.inbuf->sd = net_io; |
|
184 sn->sn.inbuf->pos = 0; |
|
185 |
|
186 /* prepare buffer */ |
|
187 int cur_input_len = nb->cursize - nb->pos; |
|
188 if(cur_input_len >= ctlen) { |
|
189 |
|
190 /* |
|
191 * all data is already in the primary input buffer |
|
192 * just link the new netbuf to the primary buffer |
|
193 */ |
|
194 sn->sn.inbuf->maxsize = ctlen; |
|
195 sn->sn.inbuf->cursize = ctlen; |
|
196 sn->sn.inbuf->inbuf = nb->inbuf + nb->pos; |
|
197 } else { |
|
198 sn->sn.inbuf->maxsize = (ctlen > 2048) ? (2048) : (ctlen); |
|
199 sn->sn.inbuf->inbuf = malloc(sizeof(sn->sn.inbuf->maxsize)); |
|
200 |
|
201 if(cur_input_len > 0) { |
|
202 /* we have read a part of the request body -> copy to netbuf */ |
|
203 memcpy(sn->sn.inbuf->inbuf, nb->inbuf+nb->pos, cur_input_len); |
|
204 } |
|
205 |
|
206 sn->sn.inbuf->cursize = cur_input_len; |
|
207 } |
|
208 } else { |
|
209 sn->sn.inbuf = NULL; |
|
210 } |
|
211 |
|
212 |
|
213 // Send the request to the NSAPI system |
|
214 nsapi_handle_request(sn, rq); |
|
215 |
|
216 return 0; |
|
217 } |
|
218 |
|
219 |
|
220 |
|
221 void header_add(HeaderArray *hd, char *name, char *value) { |
|
222 while(hd->len >= hd->alloc) { |
|
223 if(hd->next == NULL) { |
|
224 HeaderArray *block = malloc(sizeof(HeaderArray)); |
|
225 block->next = NULL; |
|
226 block->len = 0; |
|
227 block->headers = calloc(16, sizeof(Header)); |
|
228 block->alloc = 16; |
|
229 hd->next = block; |
|
230 } |
|
231 hd = hd->next; |
|
232 } |
|
233 hd->headers[hd->len].name = name; |
|
234 hd->headers[hd->len].value = value; |
|
235 hd->len++; |
|
236 } |
|
237 |
|
238 |
|
239 /* |
|
240 * NSAPI Processing |
|
241 * TODO: add this to new file |
|
242 */ |
|
243 |
|
244 int nsapi_handle_request(NSAPISession *sn, NSAPIRequest *rq) { |
|
245 // TODO: threadpool |
|
246 |
|
247 int r = REQ_NOACTION; |
|
248 |
|
249 do { |
|
250 switch(rq->phase) { |
|
251 case NSAPIAuthTrans: { |
|
252 rq->phase++; |
|
253 nsapi_context_next_stage(&rq->context); |
|
254 } |
|
255 case NSAPINameTrans: { |
|
256 printf(">>> NameTrans\n"); |
|
257 r = nsapi_nametrans(sn, rq); |
|
258 if(r != REQ_PROCEED) { |
|
259 break; |
|
260 } |
|
261 rq->phase++; |
|
262 nsapi_context_next_stage(&rq->context); |
|
263 } |
|
264 case NSAPIPathCheck: { |
|
265 printf(">>> PathCheck\n"); |
|
266 rq->phase++; |
|
267 nsapi_context_next_stage(&rq->context); |
|
268 } |
|
269 case NSAPIObjectType: { |
|
270 printf(">>> ObjectType\n"); |
|
271 r = nsapi_objecttype(sn, rq); |
|
272 if(r != REQ_PROCEED) { |
|
273 break; |
|
274 } |
|
275 rq->phase++; |
|
276 nsapi_context_next_stage(&rq->context); |
|
277 } |
|
278 case NSAPIService: { |
|
279 printf(">>> Service\n"); |
|
280 r = nsapi_service(sn, rq); |
|
281 if(r != REQ_PROCEED) { |
|
282 break; |
|
283 } |
|
284 rq->phase++; |
|
285 nsapi_context_next_stage(&rq->context); |
|
286 } |
|
287 case NSAPIAddLog: { |
|
288 printf(">>> AddLog\n"); |
|
289 rq->phase++; |
|
290 nsapi_context_next_stage(&rq->context); |
|
291 } |
|
292 case REQ_FINISH: { |
|
293 printf(">>> Finish\n"); |
|
294 r = nsapi_finish_request(sn, rq); |
|
295 } |
|
296 } |
|
297 } while (r == REQ_RESTART); |
|
298 |
|
299 |
|
300 return r; |
|
301 } |
|
302 |
|
303 int nsapi_finish_request(NSAPISession *sn, NSAPIRequest *rq) { |
|
304 // TODO: free memory |
|
305 close(sn->sys_fd); |
|
306 |
|
307 return 0; |
|
308 } |
|
309 |
|
310 int nsapi_nametrans(NSAPISession *sn, NSAPIRequest *rq) { |
|
311 HTTPObjectConfig *objconf = rq->vs->objects; |
|
312 printf("nsapi_nametrans\n"); |
|
313 httpd_objset *objset = objset_create(sn->sn.pool); |
|
314 rq->rq.os = objset; |
|
315 /* first object in objconf is the default object TODO: make sure it is */ |
|
316 objset_add_object(sn->sn.pool, objset, objconf->objects[0]); |
|
317 |
|
318 httpd_object *obj = objset->obj[0]; /* nametrans only in default object */ |
|
319 dtable *dt = object_get_dtable(obj, NSAPINameTrans); |
|
320 |
|
321 /* execute directives */ |
|
322 int ret = rq->context.last_req_code; |
|
323 char *name = NULL; |
|
324 char *ppath = NULL; |
|
325 for(int i=NCX_DI(rq);i<dt->ndir;i++) { |
|
326 directive *d = dt->dirs[i]; |
|
327 |
|
328 ret = d->func->func(d->param, (Session*)sn, (Request*)rq); |
|
329 |
|
330 /* check for name or ppath */ |
|
331 name = pblock_findkeyval(pb_key_name, rq->rq.vars); |
|
332 ppath = pblock_findkeyval(pb_key_ppath, rq->rq.vars); |
|
333 |
|
334 /* add additional objects to the objset */ |
|
335 if(add_objects(objconf, objset, sn, rq, name, ppath) == REQ_ABORTED) { |
|
336 fprintf(stderr, "add_objects failed\n"); |
|
337 return REQ_ABORTED; |
|
338 } |
|
339 |
|
340 if(ret != REQ_NOACTION) { |
|
341 /* |
|
342 * if a saf is still processing, we need to save the context, to |
|
343 * process this object at a later time |
|
344 */ |
|
345 if(ret == REQ_PROCESSING) { |
|
346 /* save nsapi context */ |
|
347 /* add +1 to start next round with next function */ |
|
348 rq->context.dtable_index = i + 1; |
|
349 } |
|
350 |
|
351 return ret; |
|
352 } |
|
353 } |
|
354 |
|
355 /* if no function has set the ppath var, translate it to docroot */ |
|
356 if(ret == REQ_NOACTION && ppath == NULL) { |
|
357 sstr_t docroot = rq->vs->document_root; |
|
358 if(docroot.length < 1) { |
|
359 printf("docroot too short\n"); |
|
360 return REQ_ABORTED; /* docroot too short */ |
|
361 } |
|
362 /* if there is a trailing '/', remove it */ |
|
363 if(docroot.ptr[docroot.length - 1] == '/') { |
|
364 docroot.length--; |
|
365 } |
|
366 |
|
367 sstr_t uri = sstr(pblock_findkeyval(pb_key_uri, rq->rq.reqpb)); |
|
368 |
|
369 sstr_t translated; |
|
370 translated.length = docroot.length + uri.length; |
|
371 translated.ptr = alloca(translated.length + 1); |
|
372 translated = sstrncat(2, translated, docroot, uri); |
|
373 |
|
374 pblock_kvinsert( |
|
375 pb_key_ppath, |
|
376 translated.ptr, |
|
377 translated.length, |
|
378 rq->rq.vars); |
|
379 } |
|
380 |
|
381 return REQ_PROCEED; |
|
382 } |
|
383 |
|
384 int nsapi_objecttype(NSAPISession *sn, NSAPIRequest *rq) { |
|
385 printf("nsapi_objecttype\n"); |
|
386 httpd_objset *objset = rq->rq.os; |
|
387 |
|
388 if(NCX_OI(rq) == -1) { |
|
389 /* object index is undefined -> define correct object index */ |
|
390 NCX_OI(rq) = objset->pos - 1; |
|
391 } |
|
392 |
|
393 int ret = rq->context.last_req_code; |
|
394 for(int i=NCX_OI(rq);i>=0;i--) { |
|
395 httpd_object *obj = objset->obj[i]; |
|
396 dtable *dt = object_get_dtable(obj, NSAPIObjectType); |
|
397 |
|
398 // execute directives |
|
399 for(int j=0;j<dt->ndir;j++) { |
|
400 directive *d = dt->dirs[j]; |
|
401 |
|
402 ret = d->func->func(d->param, (Session*)sn, (Request*)rq); |
|
403 switch(ret) { |
|
404 case REQ_PROCEED: { |
|
405 char *type = pblock_findkeyval( |
|
406 pb_key_content_type, |
|
407 rq->rq.srvhdrs); |
|
408 if(type == NULL) { |
|
409 ret = REQ_NOACTION; |
|
410 break; |
|
411 } |
|
412 return ret; |
|
413 } |
|
414 case REQ_PROCESSING: { |
|
415 /* save nsapi context */ |
|
416 rq->context.objset_index = i; |
|
417 |
|
418 /* add +1 to start next round with next function */ |
|
419 rq->context.dtable_index = j + 1; |
|
420 return ret; |
|
421 } |
|
422 case REQ_NOACTION: { |
|
423 break; |
|
424 } |
|
425 default: { |
|
426 return ret; |
|
427 } |
|
428 } |
|
429 } |
|
430 } |
|
431 |
|
432 /* |
|
433 * No function returned with REQ_PROCEED, but we need a content type. |
|
434 * If the path ends with a '/', we set the content type to |
|
435 * 'internal/directory' so that 'index-common' can serve the content. |
|
436 * Otherwise we set the content type to text/plain |
|
437 */ |
|
438 sstr_t path = sstr(pblock_findkeyval(pb_key_ppath, rq->rq.vars)); |
|
439 sstr_t ct; |
|
440 if(path.ptr[path.length - 1] == '/') { |
|
441 /* directory */ |
|
442 ct = sstrn("internal/directory", 18); |
|
443 } else { |
|
444 ct = sstrn("text/plain", 10); |
|
445 } |
|
446 pblock_kvinsert(pb_key_content_type, ct.ptr, ct.length, rq->rq.srvhdrs); |
|
447 |
|
448 return REQ_PROCEED; |
|
449 } |
|
450 |
|
451 int nsapi_service(NSAPISession *sn, NSAPIRequest *rq) { |
|
452 printf("nsapi_service\n"); |
|
453 httpd_objset *objset = rq->rq.os; |
|
454 |
|
455 if(NCX_OI(rq) == -1) { |
|
456 NCX_OI(rq) = objset->pos - 1; |
|
457 } |
|
458 |
|
459 int ret = rq->context.last_req_code; |
|
460 char *content_type = NULL; |
|
461 for(int i=NCX_OI(rq);i>=0;i--) { |
|
462 httpd_object *obj = objset->obj[i]; |
|
463 dtable *dt = object_get_dtable(obj, NSAPIService); |
|
464 |
|
465 // execute directives |
|
466 for(int j=0;j<dt->ndir;j++) { |
|
467 directive *d = dt->dirs[j]; |
|
468 |
|
469 /* check type parameter */ |
|
470 char *dtp = pblock_findkeyval(pb_key_type, d->param); |
|
471 if(dtp) { |
|
472 /* type parameter for directive */ |
|
473 if(!content_type) { |
|
474 content_type = pblock_findkeyval( |
|
475 pb_key_content_type, |
|
476 rq->rq.srvhdrs); |
|
477 } |
|
478 /* compare types */ |
|
479 if(strcmp(dtp, content_type) != 0) { |
|
480 continue; |
|
481 } |
|
482 } |
|
483 |
|
484 ret = d->func->func(d->param, (Session*)sn, (Request*)rq); |
|
485 if(ret != REQ_NOACTION) { |
|
486 if(ret == REQ_PROCESSING) { |
|
487 /* save nsapi context */ |
|
488 rq->context.objset_index = i; |
|
489 |
|
490 /* add +1 to start next round with next function */ |
|
491 rq->context.dtable_index = j + 1; |
|
492 } |
|
493 |
|
494 return ret; |
|
495 } |
|
496 } |
|
497 } |
|
498 |
|
499 return ret; |
|
500 } |
|
501 |
|
502 /* |
|
503 * adds objects with specific name or path to the httpd_objset |
|
504 */ |
|
505 int add_objects( |
|
506 HTTPObjectConfig *objs, |
|
507 httpd_objset *os, |
|
508 NSAPISession *sn, |
|
509 NSAPIRequest *rq, |
|
510 char *name, |
|
511 char *path) |
|
512 { |
|
513 /* first, add all objects with a matching path */ |
|
514 /* TODO */ |
|
515 |
|
516 |
|
517 /* add object with object with matching name */ |
|
518 if(name == NULL) { |
|
519 return REQ_PROCEED; |
|
520 } |
|
521 |
|
522 for(int i=0;i<objs->nobj;i++) { |
|
523 httpd_object *obj = objs->objects[i]; |
|
524 |
|
525 if(obj->name && !strcmp(name, obj->name)) { |
|
526 printf("name is %s -> add object %s\n", name, obj->name); |
|
527 objset_add_object(sn->sn.pool, os, obj); |
|
528 } |
|
529 } |
|
530 |
|
531 return REQ_PROCEED; |
|
532 } |
|