xref: /plan9/sys/src/cmd/ip/httpd/hints.c (revision b85a83648eec38fe82b6f00adfd7828ceec5ee8d)
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