17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
47dd7cddfSDavid du Colombier #include "httpd.h"
580ee5cbfSDavid du Colombier #include "httpsrv.h"
67dd7cddfSDavid du Colombier
77dd7cddfSDavid du Colombier enum{ URLmax = 65536, HINTmax = 20 };
87dd7cddfSDavid du Colombier #define RECIPLOG2 1.44269504089
97dd7cddfSDavid du Colombier
107dd7cddfSDavid du Colombier char **urlname; /* array of url strings 1,...,nurl */
117dd7cddfSDavid du Colombier static int nurl;
127dd7cddfSDavid du Colombier static uint urltab[URLmax]; /* hashstr(url) 1,...,nurl */
137dd7cddfSDavid du Colombier static int urlnext[URLmax]; /* index urltab of next url in chain */
147dd7cddfSDavid du Colombier static int urlhash[URLmax]; /* initially 0, meaning empty buckets */
157dd7cddfSDavid du Colombier
167dd7cddfSDavid du Colombier typedef struct Hint {
177dd7cddfSDavid du Colombier ushort url;
187dd7cddfSDavid du Colombier uchar prob;
197dd7cddfSDavid du Colombier } Hint;
207dd7cddfSDavid du Colombier Hint *hints[URLmax];
217dd7cddfSDavid du Colombier uchar nhint[URLmax];
227dd7cddfSDavid du Colombier
237dd7cddfSDavid du Colombier vlong
Bfilelen(void * vb)247dd7cddfSDavid du Colombier Bfilelen(void *vb)
257dd7cddfSDavid du Colombier {
267dd7cddfSDavid du Colombier Biobuf *b;
277dd7cddfSDavid du Colombier vlong n;
287dd7cddfSDavid du Colombier
297dd7cddfSDavid du Colombier b = vb;
307dd7cddfSDavid du Colombier n = Bseek(b, 0L, 2);
317dd7cddfSDavid du Colombier Bseek(b, 0L, 0);
327dd7cddfSDavid du Colombier return n;
337dd7cddfSDavid du Colombier }
347dd7cddfSDavid du Colombier
357dd7cddfSDavid du Colombier static uint
hashstr(char * key)367dd7cddfSDavid du Colombier hashstr(char* key)
377dd7cddfSDavid du Colombier {
387dd7cddfSDavid du Colombier /* asu works better than pjw for urls */
397dd7cddfSDavid du Colombier uchar *k = (unsigned char*)key;
407dd7cddfSDavid du Colombier uint h = 0;
417dd7cddfSDavid du Colombier while(*k!=0)
427dd7cddfSDavid du Colombier h = 65599*h + *k++;
437dd7cddfSDavid du Colombier return h;
447dd7cddfSDavid du Colombier }
457dd7cddfSDavid du Colombier
467dd7cddfSDavid du Colombier static int
urllookup(uint url)477dd7cddfSDavid du Colombier urllookup(uint url)
487dd7cddfSDavid du Colombier {
497dd7cddfSDavid du Colombier /* returns +index into urltab, else -hash */
507dd7cddfSDavid du Colombier int j, hash;
517dd7cddfSDavid du Colombier
527dd7cddfSDavid du Colombier hash = 1 + url%(URLmax-1);
537dd7cddfSDavid du Colombier j = urlhash[hash];
54*eed6406fSDavid du Colombier for(;;){
557dd7cddfSDavid du Colombier if(j==0)
567dd7cddfSDavid du Colombier return -hash;
577dd7cddfSDavid du Colombier if(url==urltab[j])
587dd7cddfSDavid du Colombier return j;
597dd7cddfSDavid du Colombier j = urlnext[j];
607dd7cddfSDavid du Colombier }
617dd7cddfSDavid du Colombier }
627dd7cddfSDavid du Colombier
637dd7cddfSDavid du Colombier int
Bage(Biobuf * b)647dd7cddfSDavid du Colombier Bage(Biobuf *b)
657dd7cddfSDavid du Colombier {
669a747e4fSDavid du Colombier Dir *dir;
679a747e4fSDavid du Colombier long mtime;
687dd7cddfSDavid du Colombier
699a747e4fSDavid du Colombier dir = dirfstat(Bfildes(b));
709a747e4fSDavid du Colombier if(dir != nil)
719a747e4fSDavid du Colombier mtime = dir->mtime;
729a747e4fSDavid du Colombier else
739a747e4fSDavid du Colombier mtime = 0;
749a747e4fSDavid du Colombier free(dir);
759a747e4fSDavid du Colombier return time(nil) - mtime;
767dd7cddfSDavid du Colombier }
777dd7cddfSDavid du Colombier
787dd7cddfSDavid du Colombier void
urlinit(void)797dd7cddfSDavid du Colombier urlinit(void)
807dd7cddfSDavid du Colombier {
817dd7cddfSDavid du Colombier static Biobuf *b = nil;
827dd7cddfSDavid du Colombier static vlong filelen = 0;
837dd7cddfSDavid du Colombier vlong newlen;
847dd7cddfSDavid du Colombier char *s, *arena;
857dd7cddfSDavid du Colombier int i, j, n;
867dd7cddfSDavid du Colombier uint url;
877dd7cddfSDavid du Colombier char *file;
887dd7cddfSDavid du Colombier
893ff48bf5SDavid du Colombier if(filelen < 0)
903ff48bf5SDavid du Colombier return;
917dd7cddfSDavid du Colombier file = "/sys/log/httpd/url";
927dd7cddfSDavid du Colombier if(b == nil){
937dd7cddfSDavid du Colombier b = Bopen(file, OREAD); /* first time */
947dd7cddfSDavid du Colombier if(b == nil){
957dd7cddfSDavid du Colombier syslog(0, HTTPLOG, "no %s, abandon prefetch hints", file);
967dd7cddfSDavid du Colombier filelen = -1;
977dd7cddfSDavid du Colombier return;
987dd7cddfSDavid du Colombier }
997dd7cddfSDavid du Colombier }
1007dd7cddfSDavid du Colombier newlen = Bfilelen(b); /* side effect: rewinds b */
1017dd7cddfSDavid du Colombier if(newlen == filelen || Bage(b)<300)
1027dd7cddfSDavid du Colombier return;
1037dd7cddfSDavid du Colombier filelen = newlen;
1043ff48bf5SDavid du Colombier if(filelen < 0)
1053ff48bf5SDavid du Colombier return;
1067dd7cddfSDavid du Colombier if(nurl){ /* free existing tables */
1077dd7cddfSDavid du Colombier free(urlname[0]); /* arena */
1087dd7cddfSDavid du Colombier memset(urlhash,0,sizeof urlhash);
1097dd7cddfSDavid du Colombier memset(urlnext,0,sizeof urlnext);
1107dd7cddfSDavid du Colombier nurl = 0;
1117dd7cddfSDavid du Colombier }
1127dd7cddfSDavid du Colombier if(urlname==nil)
1137dd7cddfSDavid du Colombier urlname = (char**)ezalloc(URLmax*sizeof(*urlname));
1147dd7cddfSDavid du Colombier arena = (char*)ezalloc(filelen); /* enough for all the strcpy below */
1157dd7cddfSDavid du Colombier i = 1;
1167dd7cddfSDavid du Colombier while((s=Brdline(b,'\n'))!=0){
1177dd7cddfSDavid du Colombier /* read lines of the form: 999 /url/path */
1187dd7cddfSDavid du Colombier n = Blinelen(b) - 1;
1197dd7cddfSDavid du Colombier if(n>2 && s[n]=='\n'){
1207dd7cddfSDavid du Colombier s[n] = '\0';
1217dd7cddfSDavid du Colombier }else{
1227dd7cddfSDavid du Colombier sysfatal("missing fields or newline in url-db");
1237dd7cddfSDavid du Colombier }
1247dd7cddfSDavid du Colombier j = strtoul(s,&s,10);
1257dd7cddfSDavid du Colombier while(*s==' ')
1267dd7cddfSDavid du Colombier s++;
1277dd7cddfSDavid du Colombier if(i++!=j)
1287dd7cddfSDavid du Colombier sysfatal("url-db synchronization error");
1297dd7cddfSDavid du Colombier url = hashstr(s);
1307dd7cddfSDavid du Colombier j = urllookup(url);
1317dd7cddfSDavid du Colombier if(j>=0)
1327dd7cddfSDavid du Colombier sysfatal("duplicate url");
1337dd7cddfSDavid du Colombier j = -j;
1347dd7cddfSDavid du Colombier nurl++;
1357dd7cddfSDavid du Colombier if(nurl>=URLmax){
1367dd7cddfSDavid du Colombier syslog(0, HTTPLOG, "urlinit overflow at %s",s);
1377dd7cddfSDavid du Colombier free(urlname[0]); /* arena */
1387dd7cddfSDavid du Colombier memset(urlhash,0,sizeof urlhash);
1397dd7cddfSDavid du Colombier memset(urlnext,0,sizeof urlnext);
1407dd7cddfSDavid du Colombier nurl = 0;
1417dd7cddfSDavid du Colombier return;
1427dd7cddfSDavid du Colombier }
1437dd7cddfSDavid du Colombier urltab[nurl] = url;
1447dd7cddfSDavid du Colombier urlnext[nurl] = urlhash[j];
1457dd7cddfSDavid du Colombier urlhash[j] = nurl;
1467dd7cddfSDavid du Colombier strcpy(arena,s);
1477dd7cddfSDavid du Colombier urlname[nurl] = arena;
1487dd7cddfSDavid du Colombier arena += strlen(s)+1;
1497dd7cddfSDavid du Colombier }
1507dd7cddfSDavid du Colombier syslog(0, HTTPLOG, "prefetch-hints url=%d (%.1fMB)", nurl, 1.e-6*(URLmax*sizeof(*urlname)+filelen));
1517dd7cddfSDavid du Colombier /* b is held open, because namespace will be chopped */
1527dd7cddfSDavid du Colombier }
1537dd7cddfSDavid du Colombier
1547dd7cddfSDavid du Colombier void
statsinit(void)1557dd7cddfSDavid du Colombier statsinit(void)
1567dd7cddfSDavid du Colombier {
1577dd7cddfSDavid du Colombier static Biobuf *b = nil;
1587dd7cddfSDavid du Colombier static vlong filelen = 0;
1597dd7cddfSDavid du Colombier vlong newlen;
1607dd7cddfSDavid du Colombier int iq, n, i, nstats = 0;
1617dd7cddfSDavid du Colombier uchar *s, buf[3+HINTmax*3]; /* iq, n, (url,prob)... */
1627dd7cddfSDavid du Colombier Hint *arena, *h;
1637dd7cddfSDavid du Colombier char *file;
1647dd7cddfSDavid du Colombier static void *oldarena = nil;
1657dd7cddfSDavid du Colombier
1667dd7cddfSDavid du Colombier file = "/sys/log/httpd/pathstat";
1677dd7cddfSDavid du Colombier if(b == nil){
1687dd7cddfSDavid du Colombier if(filelen == -1)
1697dd7cddfSDavid du Colombier return; /* if failed first time */
1707dd7cddfSDavid du Colombier b = Bopen(file, OREAD); /* first time */
1717dd7cddfSDavid du Colombier if(b == nil){
1727dd7cddfSDavid du Colombier syslog(0, HTTPLOG, "no %s, abandon prefetch hints", file);
1737dd7cddfSDavid du Colombier filelen = -1;
1747dd7cddfSDavid du Colombier return;
1757dd7cddfSDavid du Colombier }
1767dd7cddfSDavid du Colombier }
1777dd7cddfSDavid du Colombier newlen = Bfilelen(b); /* side effect: rewinds b */
1787dd7cddfSDavid du Colombier if(newlen == filelen || Bage(b)<300)
1797dd7cddfSDavid du Colombier return;
1807dd7cddfSDavid du Colombier filelen = newlen;
1817dd7cddfSDavid du Colombier if(oldarena){
1827dd7cddfSDavid du Colombier free(oldarena);
1837dd7cddfSDavid du Colombier memset(nhint,0,sizeof nhint);
1847dd7cddfSDavid du Colombier }
1857dd7cddfSDavid du Colombier arena = (Hint*)ezalloc((filelen/3)*sizeof(Hint));
1867dd7cddfSDavid du Colombier oldarena = arena;
187*eed6406fSDavid du Colombier for(;;){
1887dd7cddfSDavid du Colombier i = Bread(b,buf,3);
1897dd7cddfSDavid du Colombier if(i<3)
1907dd7cddfSDavid du Colombier break;
1917dd7cddfSDavid du Colombier nstats++;
1927dd7cddfSDavid du Colombier iq = buf[0];
1937dd7cddfSDavid du Colombier iq = (iq<<8) | buf[1];
1947dd7cddfSDavid du Colombier n = buf[2];
1957dd7cddfSDavid du Colombier h = arena;
1967dd7cddfSDavid du Colombier arena += n;
1977dd7cddfSDavid du Colombier hints[iq] = h;
1987dd7cddfSDavid du Colombier nhint[iq] = n;
1997dd7cddfSDavid du Colombier if(Bread(b,buf,3*n)!=3*n)
2007dd7cddfSDavid du Colombier sysfatal("stats read error");
2017dd7cddfSDavid du Colombier for(i=0; i<n; i++){
2027dd7cddfSDavid du Colombier s = &buf[3*i];
2037dd7cddfSDavid du Colombier h[i].url = (s[0]<<8) | s[1];
2047dd7cddfSDavid du Colombier h[i].prob = s[2];
2057dd7cddfSDavid du Colombier }
2067dd7cddfSDavid du Colombier }
2077dd7cddfSDavid du Colombier syslog(0, HTTPLOG, "prefetch-hints stats=%d (%.1fMB)", nstats, 1.e-6*((filelen/3)*sizeof(Hint)));
2087dd7cddfSDavid du Colombier }
2097dd7cddfSDavid du Colombier
2107dd7cddfSDavid du Colombier void
urlcanon(char * url)2117dd7cddfSDavid du Colombier urlcanon(char *url)
2127dd7cddfSDavid du Colombier {
2137dd7cddfSDavid du Colombier /* all the changes here can be implemented by rewriting in-place */
2147dd7cddfSDavid du Colombier char *p, *q;
2157dd7cddfSDavid du Colombier
2167dd7cddfSDavid du Colombier /* remove extraneous '/' in the middle and at the end */
2177dd7cddfSDavid du Colombier p = url+1; /* first char needs no change */
2187dd7cddfSDavid du Colombier q = p;
2197dd7cddfSDavid du Colombier while(q[0]){
2207dd7cddfSDavid du Colombier if(q[0]=='/' && q[-1]=='/'){
2217dd7cddfSDavid du Colombier q++;
2227dd7cddfSDavid du Colombier continue;
2237dd7cddfSDavid du Colombier }
2247dd7cddfSDavid du Colombier *p++ = *q++;
2257dd7cddfSDavid du Colombier }
2267dd7cddfSDavid du Colombier if(q[-1]=='/'){ /* trailing '/' */
2277dd7cddfSDavid du Colombier p[-1] = '\0';
2287dd7cddfSDavid du Colombier }else{
2297dd7cddfSDavid du Colombier p[0] = '\0';
2307dd7cddfSDavid du Colombier }
2317dd7cddfSDavid du Colombier
2327dd7cddfSDavid du Colombier /* specific to the cm.bell-labs.com web site */
2337dd7cddfSDavid du Colombier if(strncmp(url,"/cm/",4)==0){
2347dd7cddfSDavid du Colombier if(strchr("cims",url[4]) && strncmp(url+5,"s/who/",6)==0)
2357dd7cddfSDavid du Colombier /* strip off /cm/cs */
2367dd7cddfSDavid du Colombier memmove(url,url+6,strlen(url+6)+1);
2377dd7cddfSDavid du Colombier else if(strncmp(url+4,"ms/what/wavelet",15)==0)
2387dd7cddfSDavid du Colombier /* /cm/ms/what */
2397dd7cddfSDavid du Colombier memmove(url,url+11,strlen(url+11)+1);
2407dd7cddfSDavid du Colombier }
2417dd7cddfSDavid du Colombier }
2427dd7cddfSDavid du Colombier
2437dd7cddfSDavid du Colombier void
hintprint(HConnect * hc,Hio * hout,char * uri,int thresh,int havej)24480ee5cbfSDavid du Colombier hintprint(HConnect *hc, Hio *hout, char *uri, int thresh, int havej)
2457dd7cddfSDavid du Colombier {
2467dd7cddfSDavid du Colombier int i, j, pr, prefix, fd, siz, havei, newhint = 0, n;
2477dd7cddfSDavid du Colombier char *query, *sf, etag[32], *wurl;
2489a747e4fSDavid du Colombier Dir *dir;
2497dd7cddfSDavid du Colombier Hint *h, *haveh;
2507dd7cddfSDavid du Colombier
25180ee5cbfSDavid du Colombier query = hstrdup(hc, uri);
2527dd7cddfSDavid du Colombier urlcanon(query);
2537dd7cddfSDavid du Colombier j = urllookup(hashstr(query));
2547dd7cddfSDavid du Colombier if(j < 0)
2557dd7cddfSDavid du Colombier return;
2567dd7cddfSDavid du Colombier query = strrchr(uri,'/');
2577dd7cddfSDavid du Colombier if(!query)
2587dd7cddfSDavid du Colombier return; /* can't happen */
2597dd7cddfSDavid du Colombier prefix = query-uri+1; /* = strlen(dirname)+1 */
2607dd7cddfSDavid du Colombier h = hints[j];
2617dd7cddfSDavid du Colombier for(i=0; i<nhint[j]; i++){
2627dd7cddfSDavid du Colombier if(havej > 0 && havej < URLmax){ /* exclude hints client has */
2637dd7cddfSDavid du Colombier haveh = hints[havej];
2647dd7cddfSDavid du Colombier for(havei=0; havei<nhint[havej]; havei++)
2657dd7cddfSDavid du Colombier if( haveh[havei].url == h[i].url)
2667dd7cddfSDavid du Colombier goto continuei;
2677dd7cddfSDavid du Colombier }
2687dd7cddfSDavid du Colombier sf = urlname[h[i].url];
2697dd7cddfSDavid du Colombier pr = h[i].prob;
2707dd7cddfSDavid du Colombier if(pr<thresh)
2717dd7cddfSDavid du Colombier break;
2727dd7cddfSDavid du Colombier n = strlen(webroot) + strlen(sf) + 1;
27380ee5cbfSDavid du Colombier wurl = halloc(hc, n);
2747dd7cddfSDavid du Colombier strcpy(wurl, webroot);
2757dd7cddfSDavid du Colombier strcat(wurl, sf);
2767dd7cddfSDavid du Colombier fd = open(wurl, OREAD);
2777dd7cddfSDavid du Colombier if(fd<0)
2787dd7cddfSDavid du Colombier continue;
2799a747e4fSDavid du Colombier dir = dirfstat(fd);
2809a747e4fSDavid du Colombier if(dir == nil){
2817dd7cddfSDavid du Colombier close(fd);
2827dd7cddfSDavid du Colombier continue;
2837dd7cddfSDavid du Colombier }
2847dd7cddfSDavid du Colombier close(fd);
2859a747e4fSDavid du Colombier snprint(etag, sizeof(etag), "\"%lluxv%lux\"", dir->qid.path, dir->qid.vers);
2869a747e4fSDavid du Colombier siz = (int)( log((double)dir->length) * RECIPLOG2 + 0.9999);
2879a747e4fSDavid du Colombier free(dir);
2887dd7cddfSDavid du Colombier if(strncmp(uri,sf,prefix)==0 && strchr(sf+prefix,'/')==0 && sf[prefix]!=0)
2897dd7cddfSDavid du Colombier sf = sf+prefix;
2907dd7cddfSDavid du Colombier hprint(hout, "Fresh: %d,%s,%d,%s\r\n", pr, etag, siz, sf);
2917dd7cddfSDavid du Colombier newhint++;
2927dd7cddfSDavid du Colombier continuei: ;
2937dd7cddfSDavid du Colombier }
2947dd7cddfSDavid du Colombier if(newhint)
2957dd7cddfSDavid du Colombier hprint(hout, "Fresh: have/%d\r\n", j);
2967dd7cddfSDavid du Colombier }
2977dd7cddfSDavid du Colombier
298