2 /* -}{----------------------------------------------------------------------- */
9 /* -}{---- ------------------------------------------------------------------ */
11 #define TMPBUFSIZE 4096
12 static char tmpbuf[TMPBUFSIZE];
13 static char* hostname;
14 static k_hashtable* resources;
16 /* -}{---- ------------------------------------------------------------------ */
18 typedef struct ni_driver{
20 ni_handles_resource handles_resource;
21 ni_sync_resource sync_resource;
24 /* -}{---- From headers.c --------------------------------------------------- */
26 extern void init_headers(void);
27 extern void drop_entity_headers(k_hashtable* ent_head);
28 extern void fill_headers(k_hashtable* ent_head,
38 /* -}{---- ------------------------------------------------------------------ */
40 static void incoming_request(ni_event* evq);
41 static ni_resource* ensure_res(char* pub);
42 static void ensure_sub_entry(k_hashtable* ent_head, k_hashtable* sub);
43 static void ensure_pub_entry(k_hashtable* ent_head, k_hashtable* sub);
44 static k_hashtable* get_this(k_hashtable*, char*, k_hashtable*);
45 static k_hashtable* get_using(k_hashtable* curr, char* tag, char* val);
46 static void post_to_driver(char* pub, ni_event* evq);
47 static void incoming_resource(ni_event* evt);
48 static void test_pub_tos(ni_resource* res, ni_event* evt, int entityok);
49 static int satisfiable(k_hashtable*, ni_resource*, ni_event*, int);
50 static void update_pubcache(int, k_hashtable**, k_hashtable*, k_hashtable*);
51 static int sub_less(k_hashtable* sub1, k_hashtable* sub2);
52 static void fix_via_subs(k_hashtable* evteh, k_hashtable* reseh);
53 static int sub_ok(k_hashtable* sub);
54 static int range_ok(char* range, char* conrange);
55 static void merge_non_entity_headers(k_hashtable* reseh, k_hashtable* evteh);
56 static void merge_entity_range(ni_resource* res, ni_event* evt);
57 static void respond( k_hashtable* sub, ni_resource* res, ni_event* evt);
58 static void respond_ok(k_hashtable* sub, ni_event* evv);
59 static void respond_nf(k_hashtable* sub, ni_event* evv);
60 static void* entity_to_octets(k_hashtable* ent_head, void* entity);
61 static char* handled_by(char* pub);
62 static void call_sync_resource(ni_resource* res);
63 static ni_driver* ni_driver_new(char* name,
64 ni_handles_resource handles_resource,
65 ni_sync_resource sync_resource);
67 /* -}{---- ------------------------------------------------------------------ */
69 EXPORT int ni_module_loaded(void)
71 resources=k_hashtable_new("Resources", 0);
75 k_log_out("NI initialised");
79 EXPORT void ni_module_tick(void)
83 EXPORT int ni_module_event(void* data)
87 if(!k_hashtable_get(evt->ent_head, "Status:")){
89 incoming_request(evt);
92 incoming_resource(evt);
97 /* -}{---- ------------------------------------------------------------------ */
99 void incoming_request(ni_event* evq)
102 if(L) ni_event_show(evq, "NI got request event");
104 k_hashtable* sub=evq->ent_head;
105 char* uri =k_hashtable_get(sub, "URI:");
106 char* pub =k_hashtable_get(sub, "Sub-To:"); if(!pub) return;
107 int styor=k_hashtable_isi(sub, "Sub-Type:", "Original");
108 char* via =k_hashtable_get(sub, "Via:");
109 char* from =k_hashtable_get(sub, "From:");
110 if(L) k_log_out("------------------ %s", pub);
112 int nocache=k_hashtable_isi(sub, "Cache-Control:", "no-cache");
114 k_hashtable_remove( sub, "Cache-Control:");
115 k_hashtable_set( sub, "If-Modified-Since:", "0");
119 ni_resource* ses=k_hashtable_get(resources, uri);
120 if(ses) ensure_sub_entry(ses->ent_head, sub);
121 if(ses) if(L) ni_resource_show(ses, "Subscribing Resource:");
123 post_to_driver(pub, evq);
128 ni_resource* res=ensure_res(pub);
129 if(L) ni_resource_show(res, "Publishing Resource:");
131 k_hashtable* pubcache=0;
132 int sat=satisfiable(sub, res, 0, 0);
133 update_pubcache(sat, &pubcache, res->ent_head, sub);
135 if(L) k_log_out("Memory cache hit for %s", pub);
136 respond(k_hashtable_dup(sub), res, 0);
137 ni_event_delete(evq);
140 ensure_pub_entry(res->ent_head, sub);
141 if(L) ni_resource_show(res, "Pending Resource:");
143 evq->ent_head=pubcache;
144 post_to_driver(pub, evq);
145 k_hashtable_delete(sub);
148 if(L) k_log_out("In-progress Pub-Cache sufficient");
149 ni_event_delete(evq);
154 ni_resource* ensure_res(char* pub)
156 ni_resource* res=k_hashtable_get(resources, pub);
158 res=ni_resource_new(pub, k_hashtable_new("resHeaders", 1), 0);
159 k_hashtable_set(resources, pub, res);
164 void ensure_sub_entry(k_hashtable* ent_head, k_hashtable* sub)
167 k_hashtable* sup=get_this(ent_head, tag, sub);
169 k_hashtable* sup=k_hashtable_dup(sub);
170 char* uri=k_hashtable_extract(sup, "Sub-To:");
171 k_hashtable_put( sup, "URI:", uri);
172 k_hashtable_remove( sup, "Sub-Type:");
173 k_hashtable_sub(ent_head, tag, sup);
176 k_hashtable_remove(sup, "Range:");
177 k_hashtable_remove(sup, "Content-Range:");
178 k_hashtable_remove(sup, "Status:");
179 k_hashtable_remove(sup, "Status-Cache:");
180 char* mth=k_strdup(k_hashtable_get(sub, "Method:"));
181 char* via=k_strdup(k_hashtable_get(sub, "Via:"));
182 char* ims=k_strdup(k_hashtable_get(sub, "If-Modified-Since:"));
183 char* rng=k_strdup(k_hashtable_get(sub, "Range:"));
184 if(mth) k_hashtable_put(sup, "Method:", mth);
185 if(via) k_hashtable_put(sup, "Via:", via);
186 if(ims) k_hashtable_put(sup, "If-Modified-Since:", ims);
187 if(rng) k_hashtable_put(sup, "Range:", rng);
191 void ensure_pub_entry(k_hashtable* ent_head, k_hashtable* sub)
194 k_hashtable* sup=get_this(ent_head, tag, sub);
196 k_hashtable* sup=k_hashtable_dup(sub);
197 k_hashtable_remove(sup, "Sub-To:");
198 k_hashtable_remove(sup, "Sub-Type:");
199 k_hashtable_sub(ent_head, tag, sup);
202 k_hashtable_remove(sup, "Method:");
203 k_hashtable_remove(sup, "If-Modified-Since:");
204 k_hashtable_remove(sup, "Cache-Control:");
205 k_hashtable_remove(sup, "Update:");
206 char* mth=k_strdup(k_hashtable_get(sub, "Method:"));
207 char* ims=k_strdup(k_hashtable_get(sub, "If-Modified-Since:"));
208 char* ccn=k_strdup(k_hashtable_get(sub, "Cache-Control:"));
209 char* upd=k_strdup(k_hashtable_get(sub, "Update:"));
210 if(mth) k_hashtable_put(sup, "Method:", mth);
211 if(ims) k_hashtable_put(sup, "If-Modified-Since:", ims);
212 if(ccn) k_hashtable_put(sup, "Cache-Control:", ccn);
213 if(upd) k_hashtable_put(sup, "Update:", upd);
217 k_hashtable* get_this(k_hashtable* ent_head, char* pubsub, k_hashtable* try)
219 k_hashtable* curr=k_hashtable_get(ent_head, pubsub);
226 val=k_hashtable_get(try, tag);
227 if(val) return get_using(curr, tag, val);
230 val=k_hashtable_get(try, tag);
231 if(val) return get_using(curr, tag, val);
236 k_hashtable* get_using(k_hashtable* curr, char* tag, char* val)
239 for(c=curr; c; c=c->next) if(k_hashtable_is(c, tag, val)) return c;
243 /* -}{---- ------------------------------------------------------------------ */
245 void incoming_resource(ni_event* evt)
248 if(L) ni_event_show(evt, "ni got resource incoming event");
250 ni_resource* res=k_hashtable_get(resources, evt->uri);
251 int fullconst=res && k_hashtable_is(res->ent_head, "Status:", "200") &&
252 k_hashtable_is(res->ent_head, "CUX:", "C");
255 k_log_err("Resource is complete and Constant");
256 ni_event_delete(evt);
259 if(!res) res=ensure_res(evt->uri);
261 k_hashtable* reseh=res->ent_head;
262 k_hashtable* evteh=evt->ent_head;
263 int okfull =k_hashtable_is(evteh, "Status:", "200");
264 int partial =k_hashtable_is(evteh, "Status:", "206");
265 int headonly=k_hashtable_is(evteh, "Status:", "260");
266 int updated =k_hashtable_is(evteh, "Status:", "266");
267 int notmod =k_hashtable_is(evteh, "Status:", "304");
268 int notfound=k_hashtable_is(evteh, "Status:", "404");
270 int entityok= (okfull || partial || notmod);
271 int entityev=!(headonly || notmod || notfound);
272 int entityin=!!evt->entity;
273 int entityon=!!res->entity;
277 if(okfull || !entityon){
278 k_hashtable_merge(reseh, evteh);
279 if(okfull) k_hashtable_remove(reseh, "Content-Range:");
282 merge_non_entity_headers(reseh, evteh);
285 fix_via_subs(evteh, reseh);
288 if(okfull || (partial && !entityon)){
289 if(res->entity!=evt->entity){
290 if(!k_hashtable_is(reseh, "CUX:", "C")){
293 res->entity=evt->entity;
297 if(partial && entityon){
298 merge_entity_range(res, evt);
302 if(L) ni_resource_show(res, "updated resource - sync and try pubs:");
304 call_sync_resource(res);
306 test_pub_tos(res, evt, entityok);
309 ni_event_delete(evt);
311 if(L) ni_resource_show(res, "resource after ni:");
314 void fix_via_subs(k_hashtable* evteh, k_hashtable* reseh)
316 char* from=k_hashtable_get(evteh, "From:");
318 k_hashtable* subs=k_hashtable_get(reseh, "Sub-To:");
320 for(sub=subs; sub; sub=sub->next){
321 if(!k_hashtable_is(sub, "Via:", from)) continue;
322 int st=k_hashtable_get_int(evteh, "Status:");
323 char* cr=k_hashtable_get_dup(evteh, "Content-Range:");
324 if(st!=304) k_hashtable_put_int(sub, "Status:", st);
325 if(st==200) k_hashtable_remove( sub, "Content-Range:");
327 if(cr) k_hashtable_put(sub, "Content-Range:", cr);
328 if(sub_ok(sub)) k_hashtable_set(sub, "Status-Cache:", "OK");
332 void test_pub_tos(ni_resource* res, ni_event* evt, int entityok)
334 k_hashtable* keeps=0;
335 k_hashtable* sub=k_hashtable_extract(res->ent_head, "Pub-To:");
337 k_hashtable* subnext=sub->next;
340 int sat=satisfiable(sub, res, evt, entityok);
341 update_pubcache(sat, 0, res->ent_head, sub);
343 respond(k_hashtable_dup(sub), res, evt);
344 if(!k_hashtable_get(sub, "Update:")) keep=0;
351 k_hashtable_delete(sub);
357 k_hashtable* subnext=sub->next;
358 k_hashtable_sub(res->ent_head, "Pub-To:", sub);
363 int satisfiable(k_hashtable* sub,
369 int get =k_hashtable_is( sub, "Method:", "GET");
370 int head =k_hashtable_is( sub, "Method:", "HEAD");
371 int dosub=k_hashtable_is( sub, "Method:", "SUB");
372 int unsub=k_hashtable_is( sub, "Method:", "UNSUB");
373 int full=!k_hashtable_isi(sub, "Cache-Control:", "no-full");
374 int updt =k_hashtable_isi(sub, "Update:", "changes");
375 char* ims =k_hashtable_get(sub, "If-Modified-Since:");
376 char* range=k_hashtable_get(sub, "Range:");
378 int getsub =get || dosub;
379 int getrange=getsub && range;
381 k_hashtable* reseh=res->ent_head;
382 int gotall= k_hashtable_is( reseh, "Status:", "200");
383 int gotrange= k_hashtable_is( reseh, "Status:", "206");
384 int gothead= k_hashtable_is( reseh, "Status:", "260");
385 int gotupdated=evt &&
386 k_hashtable_is(evt->ent_head, "Status:", "266");
387 int notfound= k_hashtable_is( reseh, "Status:", "404");
388 int constant= k_hashtable_is( reseh, "CUX:", "C" );
389 char* conrange= k_hashtable_get(reseh, "Content-Range:");
392 if(notfound) return 1;
394 int gotany=(gothead || gotrange || gotall);
395 int snapok=(constant || !ims || entityok);
399 ((getsub && gotall && snapok) ||
400 (getrange && gotrange && range_ok(range, conrange) && snapok) ||
404 ((dosub && gotupdated)));
406 if(L) k_log_out("satisfiable %d", sat);
410 void update_pubcache(int sat,
411 k_hashtable** pubcachep,
415 if(!sat){ if(pubcachep){
416 k_hashtable* pubcache;
417 pubcache=k_hashtable_get(reseh, "Pub-Cache:");
418 if(!pubcache || sub_less(pubcache, sub)){
420 pubcache=k_hashtable_dup(sub);
421 k_hashtable_sub(reseh, "Pub-Cache:", pubcache);
424 k_hashtable_merge(pubcache, sub);
426 k_hashtable_remove(pubcache, "URI:");
427 k_hashtable_remove(pubcache, "Sub-Type:");
428 *pubcachep=k_hashtable_dup(pubcache);
429 k_hashtable_set(*pubcachep, "Sub-Type:", "Cache");
432 else{ if(!pubcachep){
433 k_hashtable* pubcache;
434 pubcache=k_hashtable_get(reseh, "Pub-Cache:");
435 if(!sub_less(sub, pubcache)){
436 k_hashtable_remove(pubcache, "Method:");
437 k_hashtable_remove(pubcache, "If-Modified-Since:");
442 int sub_ok(k_hashtable* sub)
444 char* gotresp =k_hashtable_get(sub, "Status:");
445 if(!gotresp) return 0;
446 int notfound=k_hashtable_is( sub, "Status:", "404");
447 if(notfound) return 0;
448 int gotall =k_hashtable_is( sub, "Status:", "200");
449 int gotrange=k_hashtable_is( sub, "Status:", "206");
450 int gothead= k_hashtable_is( sub, "Status:", "260");
451 int doget =k_hashtable_is( sub, "Method:", "GET");
452 int dohead =k_hashtable_is( sub, "Method:", "HEAD");
453 int dosub =k_hashtable_is( sub, "Method:", "SUB");
454 char* range =k_hashtable_get(sub, "Range:");
455 char* conrange=k_hashtable_get(sub, "Content-Range:");
457 int dogetsub=doget || dosub;
458 int dogetrange=dogetsub && range;
459 int gotany =(gothead || gotrange || gotall);
461 return (dogetsub && gotall ) ||
462 (dogetrange && gotrange && range_ok(range, conrange)) ||
466 int sub_less(k_hashtable* sub1, k_hashtable* sub2)
469 if(L) k_log_out("sub_less sub1:");
470 if(L) k_hashtable_show_chars(sub1);
471 if(L) k_log_out("sub_less sub2:");
472 if(L) k_hashtable_show_chars(sub2);
473 char* mth1 =k_hashtable_get(sub1, "Method:");
474 int head1=k_hashtable_is( sub1, "Method:", "HEAD");
475 int get2 =k_hashtable_is( sub2, "Method:", "GET");
476 char* ims1 =k_hashtable_get(sub1, "If-Modified-Since:");
477 char* ims2 =k_hashtable_get(sub2, "If-Modified-Since:");
478 int full=!k_hashtable_isi(sub2, "Cache-Control:", "no-full");
479 return full && ((!mth1) || (head1 && get2) || (!ims1 && ims2));
482 int range_ok(char* range, char* conrange)
484 k_log_out("range_ok not implemented: %s vs %s", range, conrange);
488 void merge_non_entity_headers(k_hashtable* reseh, k_hashtable* evteh)
492 void merge_entity_range(ni_resource* res, ni_event* evt)
494 k_log_out("merge_entity_range not implemented yet");
497 /* -}{---- ------------------------------------------------------------------ */
499 void respond(k_hashtable* sub, ni_resource* res, ni_event* evt)
501 int updated=evt && k_hashtable_is(evt->ent_head, "Status:", "266");
502 ni_event* evv=updated? ni_event_dup(evt):
505 if(L) ni_event_show(evv, "respond");
507 k_hashtable_remove(evv->ent_head, "Permit:");
508 k_hashtable_remove(evv->ent_head, "Sub-To:");
509 k_hashtable_remove(evv->ent_head, "Pub-To:");
510 k_hashtable_remove(evv->ent_head, "Pub-Cache:");
511 k_hashtable_sub( evv->ent_head, "Pub-To:", sub);
514 nf=k_hashtable_is( evv->ent_head, "Status:", "404");
515 if(!nf) respond_ok(sub, evv);
516 else respond_nf(sub, evv);
519 void respond_ok(k_hashtable* sub, ni_event* evv)
525 k_hashtable* ent_head=evv->ent_head;
527 time_t modifitime=k_hashtable_get_int(ent_head, "Last-Modified-Epoch:");
528 char* modistring=k_hashtable_get( ent_head, "Last-Modified:");
530 modifitime=k_time_from_rfc(modistring);
532 char* imss=k_hashtable_get(sub, "If-Modified-Since:");
533 time_t imst=k_time_from_rfc(imss);
535 if(modifitime== -1 || imst== -1 || modifitime > imst){
536 if(k_hashtable_is(sub, "Method:", "HEAD")){
539 if(!k_hashtable_is(ent_head, "CUX:", "C")){
540 evv->entity=entity_to_octets(ent_head, evv->entity);
546 statustext="Not Modified";
550 drop_entity_headers(ent_head);
553 fill_headers(ent_head, status, statustext, 0,
554 modistring? -1: modifitime,
555 datalength, 0, 0, nocache);
557 char* pub =k_hashtable_get(sub, "URI:");
559 if(L) ni_event_show(evv, "respond_ok");
560 post_to_driver(pub, evv);
563 void respond_nf(k_hashtable* sub, ni_event* evv)
565 fill_headers(evv->ent_head, "404", "File Not Found", 0, -1, 0, 0, 0, 1);
566 char* pub =k_hashtable_get(sub, "URI:");
567 post_to_driver(pub, evv);
570 void post_to_driver(char* pub, ni_event* evq)
572 char* driver=handled_by(pub);
573 if(0) k_log_out("Passing %s on to %s", pub, driver);
574 if(0) ni_event_show(evq, "Post to Driver:");
575 k_event_post(driver, evq);
578 void* entity_to_octets(k_hashtable* ent_head, void* entity)
580 if(!entity) return 0;
581 size_t size=k_hashtable_get_int(ent_head, "Content-Length:");
582 return k_memdup(entity, size);
585 /* -}{---- ------------------------------------------------------------------ */
587 EXPORT char* ni_hostname()
589 return hostname? hostname: "not set";
592 EXPORT void ni_hostname_set(char* name)
595 hostname=k_strdup(name);
598 /* -}{---- ------------------------------------------------------------------ */
600 static ni_driver* npdriver;
601 static ni_driver* moddriver;
603 char* handled_by(char* pub)
605 if(moddriver->handles_resource(pub)) return moddriver->name;
609 void call_sync_resource(ni_resource* res)
611 if(moddriver->handles_resource(res->uri)) moddriver->sync_resource(res);
612 else npdriver->sync_resource(res);
615 ni_driver* ni_driver_new(char* name,
616 ni_handles_resource handles_resource,
617 ni_sync_resource sync_resource)
619 ni_driver* driver=k_malloc(sizeof(ni_driver));
621 driver->handles_resource=handles_resource;
622 driver->sync_resource =sync_resource;
626 EXPORT void ni_register_driver(char* name,
627 ni_handles_resource handles_resource,
628 ni_sync_resource sync_resource)
630 ni_driver* d=ni_driver_new(name, handles_resource, sync_resource);
631 if(!strcmp(name, "np")) npdriver=d;
635 /* -}{---- ------------------------------------------------------------------ */
637 EXPORT ni_resource* ni_resource_new(char* uri,
638 k_hashtable* ent_head,
641 char* urih=k_hashtable_get(ent_head, "URI:");
644 if(uri) k_hashtable_put_dup(ent_head, "URI:", uri);
646 ni_resource* res=k_malloc(sizeof(ni_resource));
647 res->uri =(uri? k_strdup(uri): 0);
648 res->ent_head=ent_head;
653 EXPORT ni_resource* ni_resource_dup(ni_resource* res)
655 ni_resource* rep=k_malloc(sizeof(ni_resource));
656 rep->uri =k_strdup( res->uri);
657 rep->ent_head=k_hashtable_dup(res->ent_head);
658 rep->entity = res->entity;
662 EXPORT void ni_resource_delete(ni_resource* res)
665 if(res->entity && res->ent_head){
666 int constant=k_hashtable_is(res->ent_head, "CUX:", "C");
667 if(!constant) k_free(res->entity);
669 k_hashtable_delete(res->ent_head);
674 static char* excto[]={ "Permit:", "Sub-To:", "Pub-To:", 0 };
675 static char* perto[]={ "Permit:", 0 };
676 static char* subto[]={ "Sub-To:", 0 };
677 static char* pubto[]={ "Pub-To:", 0 };
679 EXPORT void ni_resource_show(ni_resource* res, char* text)
682 k_log_out("\n---%s--------\n------------\n\n----------",
689 l+=k_hashtable_snprintf_x(res->ent_head, b+l, s-l, excto);
690 l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, perto);
691 l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, subto);
692 l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, pubto);
693 k_log_out("\n---%s----%s--\n%s----------\n%p\n----------",
694 text, res->uri, tmpbuf, res->entity);
698 /* -}{---- ------------------------------------------------------------------ */
700 EXPORT ni_resource* ni_resource_get(char* uri)
702 return k_hashtable_get(resources, uri);
705 /* -}{---- ------------------------------------------------------------------ */
707 EXPORT ni_event* ni_res_to_evt(ni_resource* res)
709 ni_event* evp=k_malloc(sizeof(ni_event));
710 evp->uri =k_strdup( res->uri);
711 evp->evt_head=k_hashtable_new("vHeaders/ni_res_to_evt", 1);
712 evp->ent_head=k_hashtable_dup(res->ent_head);
713 evp->entity = res->entity;
717 /* -}{---- ------------------------------------------------------------------ */
719 EXPORT ni_event* ni_event_new(char* uri,
720 k_hashtable* evt_head,
721 k_hashtable* ent_head,
724 char* urih=k_hashtable_get(ent_head, "URI:");
727 if(uri) k_hashtable_put_dup(ent_head, "URI:", uri);
729 ni_event* evt=k_malloc(sizeof(ni_event));
730 evt->uri =(uri? k_strdup(uri): 0);
731 evt->evt_head=evt_head;
732 evt->ent_head=ent_head;
737 EXPORT ni_event* ni_event_dup(ni_event* evt)
739 ni_event* evp=k_malloc(sizeof(ni_event));
740 evp->uri =k_strdup( evt->uri);
741 evp->evt_head=k_hashtable_dup(evt->evt_head);
742 evp->ent_head=k_hashtable_dup(evt->ent_head);
743 evp->entity = evt->entity;
747 EXPORT void ni_event_delete(ni_event* evt)
750 if(evt->entity && evt->ent_head){
751 int constant=k_hashtable_is(evt->ent_head, "CUX:", "C");
752 if(!constant) k_free(evt->entity);
754 k_hashtable_delete(evt->evt_head);
755 k_hashtable_delete(evt->ent_head);
760 EXPORT void ni_event_show(ni_event* evt, char* text)
763 k_log_out("\n---%s--------\n------------\n\n----------",
769 int n=k_hashtable_snprintf(evt->evt_head, buf, siz)+1;
770 ; k_hashtable_snprintf(evt->ent_head, buf+n, siz-n);
771 k_log_out("\n---%s----%s--\n"
775 text, evt->uri, buf, buf+n, evt->entity);
779 /* -}{----------------------------------------------------------------------- */