first import of old cilux linux platform code
[cilux] / src / drivers / np / np.c
1
2 /* -}{----------------------------------------------------------------------- */
3
4 #include <kernelapi.h>
5 #include <ni.h>
6
7 /* -}{----------------------------------------------------------------------- */
8
9 #define TMPBUFSIZE  4096
10 static char         tmpbuf[TMPBUFSIZE];
11 static k_hashtable* own_resources;
12
13 /* -}{----------------------------------------------------------------------- */
14
15 extern void  run_tests(void);
16
17 extern char*   make_cache_path(char* uri);
18 extern void    look_in_file_cache(ni_event* evq);
19 extern void    save_in_file_cache(ni_resource* res);
20
21 extern void    init_uri2chan(void);
22 extern char*   get_host_for(char* uri);
23 extern char*   get_channel_for(char* host);
24 extern char*   use_ping_info(k_hashtable*, k_channel*);
25 extern void    use_from_info(k_hashtable*, k_channel*);
26 extern void    ping_tunnels(void);
27 extern void    send_ping(k_channel* chan, char* firstline, char* to);
28
29 /* -}{----------------------------------------------------------------------- */
30
31 static int           handles_resource(char* name);
32 static void          sync_resource(ni_resource* res);
33        int           connection_writable(k_channel* chan, int bufpos, int len);
34        int           connection_readable(k_channel* chan, int bufpos, int len);
35 static int           recv_next_event(    k_channel* chan);
36 static void          recv_request(       k_channel* chan, char* header);
37 static void          recv_response(      k_channel* chan, char* header);
38 static void          got_mmap(char*, char*, char*, int, k_stat, void*);
39 static void          set_read_buffer(k_channel*, char*, size_t, ni_event*);
40 static int           recv_entity(        k_channel* chan, int bufpos, int eof);
41 static int           expecting_response(char* pub, ni_event* evt, k_channel*);
42 static void          do_request(   ni_event* evq);
43        void          ensure_self_sub(ni_event* evq);
44 static void          ping_resource_subs(void* arg, char* key, void* val);
45 static void          ping_sub(ni_resource* res, k_hashtable* sub);
46 static ni_resource* own_resource(char* uri);
47 static void          send_request(ni_event* evq);
48 static void          send_response(ni_event* evt);
49 static k_channel*    ensure_chan(char* chanm);
50
51 /* -}{----------------------------------------------------------------------- */
52
53 EXPORT int np_module_loaded(void)
54 {
55         ni_register_driver("np", handles_resource, sync_resource);
56
57         if(strstr(k_version, "test")){
58                 //run_tests();
59         }
60
61         init_uri2chan();
62
63         own_resources=k_hashtable_new("Own Resources", 0);
64
65         k_log_out("NP Driver initialised");
66
67         return 0;
68 }
69
70 EXPORT void np_module_tick(void)
71 {
72         static long tix;
73         tix++;
74         if(!(tix % 1000)){
75                 k_hashtable_apply(own_resources, ping_resource_subs, 0);
76         }
77         if(!(tix % 3000)){
78                 ping_tunnels();
79         }
80 }
81
82 EXPORT int np_module_event(void* data)
83 {
84         ni_event* evt=data;
85         if(!k_hashtable_get(evt->ent_head, "Status:")){
86                 do_request(evt);
87         }
88         else{
89                 send_response(evt);
90         }
91         return 0;
92 }
93
94 /* -}{----------------------------------------------------------------------- */
95
96 int handles_resource(char* name)
97 {
98         return 0;
99 }
100
101 void sync_resource(ni_resource* res)
102 {
103         save_in_file_cache(res);
104 }
105
106 /* -}{----------------------------------------------------------------------- */
107
108 int connection_readable(k_channel* chan, int bufpos, int len)
109 {
110         if(0) k_log_out("connection_readable %s %p %d %d %p",
111                                  chan->name, chan, bufpos, len, chan->context);
112         int sof=(len==  0);
113         int eof=(len== -1);
114
115         if(sof) return 0;
116
117         do{
118                 ni_event* evt=chan->context;
119                 if(!evt){
120                         int n=recv_next_event(chan);
121                         if(n<0) break;
122                         bufpos-=n;
123                 }
124                 else{
125                         int n=recv_entity(chan, bufpos, eof);
126                         if(n<0) break;
127                         bufpos-=n;
128                 }
129
130         } while(1);
131
132         if(eof && chan->context){
133                 ni_event* evt=chan->context;
134                 evt->entity=0;
135                 ni_event_delete(evt);
136                 chan->context=0;
137         }
138
139         return 0;
140 }
141
142 int connection_writable(k_channel* chan, int bufpos, int len)
143 {
144         if(0) k_log_out("connection_writable %p %d %d %p",
145                                        chan, bufpos, len, chan->context);
146 //if(len>20000) exit(1);
147         int sof=(len==  0);
148         int eof=(len== -1);
149
150         if(sof){
151                 send_ping(chan, "PING ni/0.5" CRLF, 0);
152                 return 0;
153         }
154
155         if(eof && chan->context){
156                 ni_event* evt=chan->context;
157                 evt->entity=0;
158                 ni_event_delete(evt);
159                 chan->context=0;
160         }
161
162         return 0;
163 }
164
165 /* -}{---- Receiving -------------------------------------------------------- */
166
167 int recv_next_event(k_channel* chan)
168 {
169         char* header=k_channel_chop_div(chan, CRLF CRLF);
170         if(!header) return -1;
171         int n=strlen(header)+strlen(CRLF CRLF);
172
173         if(!strncmp(header, "GET",  3) ||
174            !strncmp(header, "SUB",  3) ||
175            !strncmp(header, "UNSUB",5) ||
176            !strncmp(header, "HEAD", 4) ||
177            !strncmp(header, "PING", 4)   ){
178
179                 recv_request(chan, header);
180                 return n;
181         }
182         if(!strncmp(header, "ni/", 4)   ){
183
184                 recv_response(chan, header);
185                 return n;
186         }
187         k_free(header);
188         k_log_err("Failed reading request or response - closing connection");
189         k_channel_close(chan);
190         return n;
191 }
192
193 void recv_request(k_channel* chan, char* header)
194 {
195         ni_event* evq;
196         evq=ni_get_request_headers(header);
197         if(!evq){
198                 k_log_err("Failed reading request headers - closing connection");
199                 k_channel_close(chan);
200                 return;
201         }
202         if(!k_hashtable_isn(evq->evt_head, "Protocol:", "ni/", 4)){
203                 ni_event_delete(evq);
204                 k_log_err("Failed reading request not ni - closing connection");
205                 k_channel_close(chan);
206                 return;
207         }
208
209         k_hashtable* ent_head=evq->ent_head;
210         int ping=k_hashtable_is(ent_head, "Method:", "PING");
211
212         if(!evq->uri && !ping){
213                 evq->uri=k_strdup(chan->name);
214                 k_hashtable_put_dup(ent_head, "URI:", chan->name);
215         }
216
217         ni_event_show(evq, "ni Protocol Request");
218
219         if(k_hashtable_isi(evq->evt_head, "Connection:", "Keep-Alive")){
220                 chan->linger=1;
221                 if(k_hashtable_isn(ent_head, "Sub-To:", "./test", 6)){
222                         chan->linger=0;
223                 }
224         }
225         if(ping){
226                 char* from=use_ping_info(ent_head, chan);
227                 if(from) send_ping(chan, "ni/0.5 270 PING" CRLF, from);
228                 ni_event_delete(evq);
229                 return;
230         }
231         //use_from_info(ent_head, chan);
232
233         ni_event* evp=ni_event_new(evq->uri, 0, k_hashtable_dup(ent_head), 0);
234
235         ni_event_delete(evq);
236
237         k_event_post("ni", evp);
238 }
239
240 void recv_response(k_channel* chan, char* header)
241 {
242         ni_event* evt=ni_get_response_headers(header);
243         if(!evt){
244                 k_log_err("recv_response: headers failed but doing nothing!");
245                 return;
246         }
247         char*        pub=     evt->uri;
248         k_hashtable* ent_head=evt->ent_head;
249
250         if(!expecting_response(pub, evt, chan)) return;
251
252         ni_event_show(evt, "Response");
253
254         int head=k_hashtable_is(     ent_head, "Status:", "260");
255         int nmod=k_hashtable_is(     ent_head, "Status:", "304");
256         int ping=k_hashtable_is(     ent_head, "Status:", "270");
257         int cl  =k_hashtable_get_int(ent_head, "Content-Length:");
258         int entity=!(head || nmod || ping || cl==0);
259
260         if(ping){
261                 use_ping_info(ent_head, chan);
262                 ni_event_delete(evt);
263                 return;
264         }
265         use_from_info(ent_head, chan);
266
267         if(entity){
268                 k_hashtable_set(ent_head, "Status:",      "260");
269                 k_hashtable_set(ent_head, "Status-Text:", "Headers Only");
270         }
271         k_event_post("ni", evt);
272
273         if(entity){
274
275                 k_hashtable* eh=k_hashtable_new("nHeaders/recv_response", 1);
276                 char* from      =k_hashtable_get(ent_head, "From:");
277                 char* contlen   =k_hashtable_get(ent_head, "Content-Length:");
278                 char* cux       =k_hashtable_get(ent_head, "CUX:");
279                 k_hashtable_set(eh, "Status:",        "206");
280                 k_hashtable_set(eh, "Status-Text:",   "Partial Content");
281                 k_hashtable_put_dup(eh, "From:",           from);
282                 k_hashtable_put_dup(eh, "Content-Length:", contlen);
283                 k_hashtable_put_dup(eh, "CUX:",            cux);
284                 ni_event* evc=ni_event_new(pub, 0, eh, 0);
285                 chan->context=evc;
286
287                 int constant=k_hashtable_is(ent_head, "CUX:", "C");
288                 if(constant){
289                         char* path=make_cache_path(pub); if(!path) return;
290                         k_file_read(".", path, USE_MMAP, cl, got_mmap, chan);
291                 }
292                 else{
293                         char* data=k_malloc(cl);
294                         set_read_buffer(chan, data, cl, evc);
295                 }
296         }
297 }
298
299 void got_mmap(char*  basedir,
300               char*  path,
301               char*  data,
302               int    usedmmap,
303               k_stat kstat,
304               void*  context){
305
306         k_free(path);
307         k_channel* chan=context;
308         ni_event* evt=chan->context;
309         if(!evt){ k_log_err("got_mmap: evt=0"); return; }
310         if(!data || !usedmmap){ k_log_err("got_mmap: mmap failed"); return; }
311
312         size_t cl=k_hashtable_get_int(evt->ent_head, "Content-Length:");
313         set_read_buffer(chan, data, cl, evt);
314 }
315
316 void set_read_buffer(k_channel* chan, char* data, size_t cl, ni_event* evt)
317 {
318         evt->entity=data;
319         int r=k_channel_setbuf(chan, data, cl);
320         if(0) k_log_out("k_channel_setbuf %d", r);
321         if(r==BUFFER_ALREADY_SET){
322                 k_log_err("oops! k_channel_setbuf BUFFER_ALREADY_SET");
323                 return;
324         }
325         if(r==BUFFER_FILLED){
326                 k_hashtable_set(evt->ent_head, "Status:",      "200");
327                 k_hashtable_set(evt->ent_head, "Status-Text:", "OK");
328                 chan->context=0;
329                 k_event_post("ni", evt);
330         }
331 }
332
333 int recv_entity(k_channel* chan, int bufpos, int eof)
334 {
335         ni_event*   evt=chan->context;
336         k_hashtable* ent_head=evt->ent_head;
337
338         char* cls=k_hashtable_get(    ent_head, "Content-Length:");
339         int   cl =k_hashtable_get_int(ent_head, "Content-Length:");
340
341         if(!cls && !eof) return -1;
342
343         int partial=0;
344         int eofcontlen=eof && (!cls || bufpos < cl);
345         if(eofcontlen){
346                 if(cls){
347                         char* clg=k_strdup(cls);
348                         k_hashtable_put(ent_head, "Content-Length-Given:", clg);
349                         partial=1;
350                 }
351                 cl=bufpos;
352                 char b[32]; snprintf(b, 32, "%d", cl);
353                 k_hashtable_put_dup(ent_head, "Content-Length:", b);
354         }
355
356         if(bufpos < cl){
357                 if(bufpos){
358                         ni_event* evp=ni_event_dup(evt);
359                         snprintf(tmpbuf, TMPBUFSIZE, "0-%d", bufpos);
360                         char* cr=k_strdup(tmpbuf);
361                         k_hashtable_put(evp->ent_head, "Content-Range:", cr);
362                         k_event_post("ni", evp);
363                 }
364                 return -1;
365         }
366
367         static char dummy_empty_entity[0];
368         if(!k_channel_getbuf(chan)){
369                 int cn=k_hashtable_is(ent_head, "CUX:", "C");
370                 if(cl) evt->entity=k_channel_chop_len(chan, cl);
371                 else   evt->entity=cn? dummy_empty_entity: k_malloc(1);
372         }
373
374         if(!partial){
375                 k_hashtable_set(ent_head, "Status:",      "200");
376                 k_hashtable_set(ent_head, "Status-Text:", "OK");
377         }
378         chan->context=0;
379         k_event_post("ni", evt);
380
381         return cl;
382 }
383
384 int expecting_response(char* pub, ni_event* evt, k_channel* chan)
385 {
386         if(pub && 0){
387                 k_log_err("unwanted response: %s", pub);
388                 ni_event_delete(evt);
389                 k_channel_close(chan);
390                 return 0;
391         }
392         return 1;
393 }
394
395 /* -}{---- Sending ---------------------------------------------------------- */
396
397 void do_request(ni_event* evq)
398 {
399         k_hashtable* sub=evq->ent_head;
400         int tc=k_hashtable_isi(sub, "Sub-Type:", "Cache");
401         int to=k_hashtable_isi(sub, "Sub-Type:", "Original");
402
403         if(tc){
404                 char* ims=k_hashtable_get(sub, "If-Modified-Since:");
405                 if(ims) ensure_self_sub(evq);
406                 else    look_in_file_cache(evq);
407         }
408         else
409         if(to){
410                 send_request(evq);
411         }
412 }
413
414 void ensure_self_sub(ni_event* evq)
415 {
416         k_hashtable* sub=evq->ent_head;
417         char*        pub=k_hashtable_get(sub, "Sub-To:");
418
419         ni_resource* res=own_resource(pub);
420         k_hashtable*  enh=res->ent_head;
421         k_hashtable*  selfsub=k_hashtable_get(enh, "Sub-To:");
422         if(selfsub && !k_hashtable_is(selfsub, "Status-Cache:", "OK")){
423                 k_log_err("cancel selfsub as new one needed");
424         }
425
426         k_hashtable* ss=k_hashtable_dup(sub);
427         k_hashtable_remove( ss, "From:");
428         k_hashtable_put_dup(ss, "URI:",       pub);
429         k_hashtable_set(    ss, "Sub-Type:", "Original");
430         k_hashtable_put_dup(ss, "Via:",       get_host_for(pub));
431         if(k_hashtable_get( ss, "If-Modified-Since:")){
432                 char* lm=k_hashtable_get(enh, "Last-Modified:");
433                 if(!res->entity) lm=0;
434                 k_hashtable_set(ss, "If-Modified-Since:", lm? lm: "0");
435         }
436         ni_event* evs=ni_event_new(0, 0, ss, 0);
437         k_event_post("ni", evs);
438
439         ni_event_delete(evq);
440 }
441
442 void ping_resource_subs(void* arg, char* key, void* val)
443 {
444         ni_resource* res=val;
445         k_hashtable* pubcache=k_hashtable_get(res->ent_head, "Pub-Cache:");
446         if(!pubcache || !k_hashtable_get(pubcache, "Method:")) return;
447         k_hashtable* subs=k_hashtable_get(res->ent_head, "Sub-To:");
448         k_hashtable* sub;
449         for(sub=subs; sub; sub=sub->next){
450                 if(!k_hashtable_is(sub, "Status-Cache:", "OK")){
451                         if(!k_hashtable_get(sub, "Status:")){
452                                 ping_sub(res, sub);
453                         }
454                         else{
455                                 int ts=k_hashtable_get_int(sub, "Timestamp:");
456                                 if(0) k_log_out("check dried-up request: %d", ts);
457                         }
458                 }
459         }
460 }
461
462 void ping_sub(ni_resource* res, k_hashtable* sub)
463 {
464         ni_resource_show(res, "ping_resource_subs");
465
466         k_hashtable* ss=k_hashtable_dup(sub);
467
468         char* subto=k_hashtable_extract(ss, "URI:");
469         k_hashtable_put_dup(ss, "URI:",    res->uri);
470         k_hashtable_put(    ss, "Sub-To:", subto);
471         k_hashtable_set(    ss, "Sub-Type:", "Original");
472         k_hashtable_put_dup(ss, "Via:",    get_host_for(res->uri));
473
474         ni_event* evs=ni_event_new(0, 0, ss, 0);
475         k_event_post("ni", evs);
476 }
477
478 ni_resource* own_resource(char* uri)
479 {
480         ni_resource* res=k_hashtable_get(own_resources, uri);
481         if(!res){
482                 res=ni_resource_get(uri);
483                 k_hashtable_set(own_resources, uri, res);
484         }
485         return res;
486 }
487
488 void send_request(ni_event* evt)
489 {
490         k_hashtable* eh=evt->ent_head;
491         char* method=k_strdup(k_hashtable_get(eh, "Method:"));
492         char* to    =k_strdup(k_hashtable_get(eh, "Sub-To:"));
493         char* via   =k_strdup(k_hashtable_get(eh, "Via:"));
494
495         char* chanm=get_channel_for(via);
496         if(!chanm) goto free_and_return;
497
498         k_channel* chan=ensure_chan(chanm);
499         if(!chan) goto free_and_return;
500
501         ni_fix_ni_headers(eh, 0);
502         ni_request(evt, to, method, chan);
503
504         free_and_return:
505         k_free(method); k_free(to); k_free(via);
506         ni_event_delete(evt);
507 }
508
509 void send_response(ni_event* evt)
510 {
511         ni_event_show(evt, "send_response");
512
513         k_hashtable* eh=evt->ent_head;
514
515         k_hashtable* sub=k_hashtable_get(eh,  "Pub-To:");
516         char* uri       =k_hashtable_get(sub, "URI:");
517         char* from      =k_hashtable_get(sub, "From:");
518         char* method    =k_hashtable_get(sub, "Method:");
519         int   methead   =k_hashtable_is( sub, "Method:", "HEAD");
520
521         char* to=from? from: uri;
522
523         char* host=from? from: get_host_for(uri);
524         char* chanm=get_channel_for(host);
525         if(!chanm){
526                 if(0) k_log_out("no ni protocol channel %s", to);
527                 ni_event_delete(evt);
528                 return;
529         }
530
531         k_channel* chan=ensure_chan(chanm);
532         if(!chan){
533                 if(0) k_log_out("no ni protocol channel %s", to);
534                 ni_event_delete(evt);
535                 return;
536         }
537
538         k_hashtable_extract(eh, "Pub-To:");
539
540         char* protocol="ni/0.5";
541
542         ni_fix_ni_headers(eh, methead);
543         ni_response(evt, to, method, protocol, 0, chan);
544
545         k_hashtable_delete(sub);
546         evt->entity=0;
547         ni_event_delete(evt);
548 }
549
550 k_channel* ensure_chan(char* chanm)
551 {
552         k_channel* chan=k_channel_get_name(chanm);
553         if(!chan){
554                 k_log_err("Cannot find current channel for %s", chanm);
555                 k_channel_connect_name(chanm, connection_readable,
556                                               connection_writable);
557         }
558         return chan;
559 }
560
561 /* -}{----------------------------------------------------------------------- */
562