xref: /plan9/sys/src/cmd/ip/httpfile.c (revision b39189fd423aed869c5cf5189bc504918cff969b)
1e79e25eaSDavid du Colombier /* contributed by 20h@r-36.net, September 2005 */
2e79e25eaSDavid du Colombier 
3e79e25eaSDavid du Colombier #include <u.h>
4e79e25eaSDavid du Colombier #include <libc.h>
5e79e25eaSDavid du Colombier #include <bio.h>
6e79e25eaSDavid du Colombier #include <ndb.h>
7e79e25eaSDavid du Colombier #include <thread.h>
8e79e25eaSDavid du Colombier #include <fcall.h>
9e79e25eaSDavid du Colombier #include <9p.h>
10e79e25eaSDavid du Colombier #include <mp.h>
11e79e25eaSDavid du Colombier #include <libsec.h>
12e79e25eaSDavid du Colombier 
13e79e25eaSDavid du Colombier enum
14e79e25eaSDavid du Colombier {
15e79e25eaSDavid du Colombier 	Blocksize = 64*1024,
16e79e25eaSDavid du Colombier 	Stacksize = 8192,
17e79e25eaSDavid du Colombier };
18e79e25eaSDavid du Colombier 
19e79e25eaSDavid du Colombier char *host;
20e79e25eaSDavid du Colombier char *file;
21e79e25eaSDavid du Colombier char *port;
22e79e25eaSDavid du Colombier char *url;
23e79e25eaSDavid du Colombier char *get;
24e79e25eaSDavid du Colombier char *user;
25e79e25eaSDavid du Colombier char *net = "net";
26e79e25eaSDavid du Colombier 
27e79e25eaSDavid du Colombier vlong size;
28e79e25eaSDavid du Colombier int usetls;
29e79e25eaSDavid du Colombier int debug;
30e79e25eaSDavid du Colombier int ncache;
31e79e25eaSDavid du Colombier int mcache;
32e79e25eaSDavid du Colombier 
33e79e25eaSDavid du Colombier void
usage(void)34e79e25eaSDavid du Colombier usage(void)
35e79e25eaSDavid du Colombier {
36*b39189fdSDavid du Colombier 	fprint(2, "usage: httpfile [-Dd] [-c count] [-f file] [-m mtpt] [-s srvname] [-x net] url\n");
37e79e25eaSDavid du Colombier 	exits("usage");
38e79e25eaSDavid du Colombier }
39e79e25eaSDavid du Colombier 
40e79e25eaSDavid du Colombier enum
41e79e25eaSDavid du Colombier {
42e79e25eaSDavid du Colombier 	Qroot,
43e79e25eaSDavid du Colombier 	Qfile,
44e79e25eaSDavid du Colombier };
45e79e25eaSDavid du Colombier 
46e79e25eaSDavid du Colombier #define PATH(type, n)		((type)|((n)<<8))
47e79e25eaSDavid du Colombier #define TYPE(path)			((int)(path) & 0xFF)
48e79e25eaSDavid du Colombier #define NUM(path)			((uint)(path)>>8)
49e79e25eaSDavid du Colombier 
50e79e25eaSDavid du Colombier Channel *reqchan;
51e79e25eaSDavid du Colombier Channel *httpchan;
52e79e25eaSDavid du Colombier Channel *finishchan;
53e79e25eaSDavid du Colombier ulong time0;
54e79e25eaSDavid du Colombier 
55e79e25eaSDavid du Colombier typedef struct Block Block;
56e79e25eaSDavid du Colombier struct Block
57e79e25eaSDavid du Colombier {
58e79e25eaSDavid du Colombier 	uchar *p;
59e79e25eaSDavid du Colombier 	vlong off;
60e79e25eaSDavid du Colombier 	vlong len;
61e79e25eaSDavid du Colombier 	Block *link;
62e79e25eaSDavid du Colombier 	long lastuse;
63e79e25eaSDavid du Colombier 	Req *rq;
64e79e25eaSDavid du Colombier 	Req **erq;
65e79e25eaSDavid du Colombier };
66e79e25eaSDavid du Colombier 
67e79e25eaSDavid du Colombier typedef struct Blocklist Blocklist;
68e79e25eaSDavid du Colombier struct Blocklist
69e79e25eaSDavid du Colombier {
70e79e25eaSDavid du Colombier 	Block *first;
71e79e25eaSDavid du Colombier 	Block **end;
72e79e25eaSDavid du Colombier };
73e79e25eaSDavid du Colombier 
74e79e25eaSDavid du Colombier Blocklist cache;
75e79e25eaSDavid du Colombier Blocklist inprogress;
76e79e25eaSDavid du Colombier 
77e79e25eaSDavid du Colombier void
queuereq(Block * b,Req * r)78e79e25eaSDavid du Colombier queuereq(Block *b, Req *r)
79e79e25eaSDavid du Colombier {
80e79e25eaSDavid du Colombier 	if(b->rq==nil)
81e79e25eaSDavid du Colombier 		b->erq = &b->rq;
82e79e25eaSDavid du Colombier 	*b->erq = r;
83e79e25eaSDavid du Colombier 	r->aux = nil;
84e79e25eaSDavid du Colombier 	b->erq = (Req**)&r->aux;
85e79e25eaSDavid du Colombier }
86e79e25eaSDavid du Colombier 
87e79e25eaSDavid du Colombier void
addblock(Blocklist * l,Block * b)88e79e25eaSDavid du Colombier addblock(Blocklist *l, Block *b)
89e79e25eaSDavid du Colombier {
90e79e25eaSDavid du Colombier 	if(debug)
91e79e25eaSDavid du Colombier 		print("adding: %p %lld\n", b, b->off);
92e79e25eaSDavid du Colombier 
93e79e25eaSDavid du Colombier 	if(l->first == nil)
94e79e25eaSDavid du Colombier 		l->end = &l->first;
95e79e25eaSDavid du Colombier 	*l->end = b;
96e79e25eaSDavid du Colombier 	b->link = nil;
97e79e25eaSDavid du Colombier 	l->end = &b->link;
98e79e25eaSDavid du Colombier 	b->lastuse = time(0);
99e79e25eaSDavid du Colombier }
100e79e25eaSDavid du Colombier 
101e79e25eaSDavid du Colombier void
delreq(Block * b,Req * r)102e79e25eaSDavid du Colombier delreq(Block *b, Req *r)
103e79e25eaSDavid du Colombier {
104e79e25eaSDavid du Colombier 	Req **l;
105e79e25eaSDavid du Colombier 
106e79e25eaSDavid du Colombier 	for(l = &b->rq; *l; l = (Req**)&(*l)->aux){
107e79e25eaSDavid du Colombier 		if(*l == r){
108e79e25eaSDavid du Colombier 			*l = r->aux;
109e79e25eaSDavid du Colombier 			if(*l == nil)
110e79e25eaSDavid du Colombier 				b->erq = l;
111e79e25eaSDavid du Colombier 			free(r);
112e79e25eaSDavid du Colombier 			return;
113e79e25eaSDavid du Colombier 		}
114e79e25eaSDavid du Colombier 	}
115e79e25eaSDavid du Colombier }
116e79e25eaSDavid du Colombier 
117e79e25eaSDavid du Colombier void
evictblock(Blocklist * cache)118e79e25eaSDavid du Colombier evictblock(Blocklist *cache)
119e79e25eaSDavid du Colombier {
120e79e25eaSDavid du Colombier 	Block **l, **oldest, *b;
121e79e25eaSDavid du Colombier 
122e79e25eaSDavid du Colombier 	if(cache->first == nil)
123e79e25eaSDavid du Colombier 		return;
124e79e25eaSDavid du Colombier 
125e79e25eaSDavid du Colombier 	oldest = nil;
126e79e25eaSDavid du Colombier 	for(l=&cache->first; *l; l=&(*l)->link)
127e79e25eaSDavid du Colombier 		if(oldest == nil || (*oldest)->lastuse > (*l)->lastuse)
128e79e25eaSDavid du Colombier 			oldest = l;
129e79e25eaSDavid du Colombier 
130e79e25eaSDavid du Colombier 	b = *oldest;
131e79e25eaSDavid du Colombier 	*oldest = (*oldest)->link;
132e79e25eaSDavid du Colombier 	if(*oldest == nil)
133e79e25eaSDavid du Colombier 		cache->end = oldest;
134e79e25eaSDavid du Colombier 	free(b->p);
135e79e25eaSDavid du Colombier 	free(b);
136e79e25eaSDavid du Colombier 	ncache--;
137e79e25eaSDavid du Colombier }
138e79e25eaSDavid du Colombier 
139e79e25eaSDavid du Colombier Block *
findblock(Blocklist * s,vlong off)140e79e25eaSDavid du Colombier findblock(Blocklist *s, vlong off)
141e79e25eaSDavid du Colombier {
142e79e25eaSDavid du Colombier 	Block *b;
143e79e25eaSDavid du Colombier 
144e79e25eaSDavid du Colombier 	for(b = s->first; b != nil; b = b->link){
145e79e25eaSDavid du Colombier 		if(b->off <= off && off < b->off + Blocksize){
146e79e25eaSDavid du Colombier 			if(debug)
147e79e25eaSDavid du Colombier 				print("found: %lld -> %lld\n", off, b->off);
148e79e25eaSDavid du Colombier 			b->lastuse = time(0);
149e79e25eaSDavid du Colombier 			return b;
150e79e25eaSDavid du Colombier 		}
151e79e25eaSDavid du Colombier 	}
152e79e25eaSDavid du Colombier 
153e79e25eaSDavid du Colombier 	return nil;
154e79e25eaSDavid du Colombier }
155e79e25eaSDavid du Colombier 
156e79e25eaSDavid du Colombier void
readfrom(Req * r,Block * b)157e79e25eaSDavid du Colombier readfrom(Req *r, Block *b)
158e79e25eaSDavid du Colombier {
159e79e25eaSDavid du Colombier 	int d, n;
160e79e25eaSDavid du Colombier 
161e79e25eaSDavid du Colombier 	b->lastuse = time(0);
162e79e25eaSDavid du Colombier 
163e79e25eaSDavid du Colombier 	n = r->ifcall.count;
164e79e25eaSDavid du Colombier 	d = r->ifcall.offset - b->off;
165e79e25eaSDavid du Colombier 	if(b->off + d + n > b->off + b->len)
166e79e25eaSDavid du Colombier 		n = b->len - d;
167e79e25eaSDavid du Colombier 	if(debug)
168e79e25eaSDavid du Colombier 		print("Reading from: %p %d %d\n", b->p, d, n);
169e79e25eaSDavid du Colombier 	memmove(r->ofcall.data, b->p + d, n);
170e79e25eaSDavid du Colombier 	r->ofcall.count = n;
171e79e25eaSDavid du Colombier 
172e79e25eaSDavid du Colombier 	respond(r, nil);
173e79e25eaSDavid du Colombier }
174e79e25eaSDavid du Colombier 
175e79e25eaSDavid du Colombier void
hangupclient(Srv *)176e79e25eaSDavid du Colombier hangupclient(Srv*)
177e79e25eaSDavid du Colombier {
178e79e25eaSDavid du Colombier 	if(debug)
179e79e25eaSDavid du Colombier 		print("Hangup.\n");
180e79e25eaSDavid du Colombier 
181e79e25eaSDavid du Colombier 	threadexitsall("done");
182e79e25eaSDavid du Colombier }
183e79e25eaSDavid du Colombier 
184e79e25eaSDavid du Colombier int
dotls(int fd)185e79e25eaSDavid du Colombier dotls(int fd)
186e79e25eaSDavid du Colombier {
187e79e25eaSDavid du Colombier 	TLSconn conn;
188e79e25eaSDavid du Colombier 
189e79e25eaSDavid du Colombier 	if((fd=tlsClient(fd, &conn)) < 0)
190e79e25eaSDavid du Colombier 		sysfatal("tlsclient: %r");
191e79e25eaSDavid du Colombier 
192e79e25eaSDavid du Colombier 	if(conn.cert != nil)
193e79e25eaSDavid du Colombier 		free(conn.cert);
194e79e25eaSDavid du Colombier 
195e79e25eaSDavid du Colombier 	return fd;
196e79e25eaSDavid du Colombier }
197e79e25eaSDavid du Colombier 
198e79e25eaSDavid du Colombier char*
nocr(char * s)199e79e25eaSDavid du Colombier nocr(char *s)
200e79e25eaSDavid du Colombier {
201e79e25eaSDavid du Colombier 	char *r, *w;
202e79e25eaSDavid du Colombier 
203e79e25eaSDavid du Colombier 	for(r=w=s; *r; r++)
204e79e25eaSDavid du Colombier 		if(*r != '\r')
205e79e25eaSDavid du Colombier 			*w++ = *r;
206e79e25eaSDavid du Colombier 	*w = 0;
207e79e25eaSDavid du Colombier 	return s;
208e79e25eaSDavid du Colombier }
209e79e25eaSDavid du Colombier 
210e79e25eaSDavid du Colombier char*
readhttphdr(Biobuf * netbio,vlong * size)211e79e25eaSDavid du Colombier readhttphdr(Biobuf *netbio, vlong *size)
212e79e25eaSDavid du Colombier {
213e79e25eaSDavid du Colombier 	char *s, *stat;
214e79e25eaSDavid du Colombier 
215e79e25eaSDavid du Colombier 	stat = nil;
216e79e25eaSDavid du Colombier 	while((s = Brdstr(netbio, '\n', 1)) != nil && s[0] != '\r'
217e79e25eaSDavid du Colombier 			&& s[0] != '\0'){
218e79e25eaSDavid du Colombier 		if(stat == nil)
219e79e25eaSDavid du Colombier 			stat = estrdup9p(s);
220e79e25eaSDavid du Colombier 		if(strncmp(s, "Content-Length: ", 16) == 0 && size != nil)
221e79e25eaSDavid du Colombier 			*size = atoll(s + 16);
222e79e25eaSDavid du Colombier 		free(s);
223e79e25eaSDavid du Colombier 	}
224e79e25eaSDavid du Colombier 	if(stat)
225e79e25eaSDavid du Colombier 		nocr(stat);
226e79e25eaSDavid du Colombier 
227e79e25eaSDavid du Colombier 	return stat;
228e79e25eaSDavid du Colombier }
229e79e25eaSDavid du Colombier 
230e79e25eaSDavid du Colombier int
dialhttp(Biobuf * netbio)231e79e25eaSDavid du Colombier dialhttp(Biobuf *netbio)
232e79e25eaSDavid du Colombier {
233e79e25eaSDavid du Colombier 	int netfd;
234e79e25eaSDavid du Colombier 
235e79e25eaSDavid du Colombier 	netfd = dial(netmkaddr(host, net, port), 0, 0, 0);
236e79e25eaSDavid du Colombier 	if(netfd < 0)
237e79e25eaSDavid du Colombier 		sysfatal("dial: %r");
238e79e25eaSDavid du Colombier 	if(usetls)
239e79e25eaSDavid du Colombier 		netfd = dotls(netfd);
240e79e25eaSDavid du Colombier 	Binit(netbio, netfd, OREAD);
241e79e25eaSDavid du Colombier 
242e79e25eaSDavid du Colombier 	return netfd;
243e79e25eaSDavid du Colombier }
244e79e25eaSDavid du Colombier 
245e79e25eaSDavid du Colombier uchar*
getrange(Block * b)246e79e25eaSDavid du Colombier getrange(Block *b)
247e79e25eaSDavid du Colombier {
248e79e25eaSDavid du Colombier 	uchar *data;
249e79e25eaSDavid du Colombier 	char *status;
250e79e25eaSDavid du Colombier 	int netfd;
251e79e25eaSDavid du Colombier 	static Biobuf netbio;
252e79e25eaSDavid du Colombier 
253e79e25eaSDavid du Colombier 	b->len = Blocksize;
254e79e25eaSDavid du Colombier 	if(b->off + b->len > size)
255e79e25eaSDavid du Colombier 		b->len = size - b->off;
256e79e25eaSDavid du Colombier 
257e79e25eaSDavid du Colombier 	if(debug)
258e79e25eaSDavid du Colombier 		print("getrange: %lld %lld\n", b->off, b->len);
259e79e25eaSDavid du Colombier 
260e79e25eaSDavid du Colombier 	netfd = dialhttp(&netbio);
261e79e25eaSDavid du Colombier 
262e79e25eaSDavid du Colombier 	fprint(netfd,
263e79e25eaSDavid du Colombier 		"GET %s HTTP/1.1\r\n"
264e79e25eaSDavid du Colombier 		"Host: %s\r\n"
265e79e25eaSDavid du Colombier 		"Accept-Encoding:\r\n"
266e79e25eaSDavid du Colombier 		"Range: bytes=%lld-%lld\r\n"
267e79e25eaSDavid du Colombier 		"\r\n",
268e79e25eaSDavid du Colombier 		get, host, b->off, b->off+b->len);
269e79e25eaSDavid du Colombier 	Bflush(&netbio);
270e79e25eaSDavid du Colombier 
271e79e25eaSDavid du Colombier 	status = readhttphdr(&netbio, nil);
272e79e25eaSDavid du Colombier 	if(status == nil)
273e79e25eaSDavid du Colombier 		return nil;
274e79e25eaSDavid du Colombier 
275e79e25eaSDavid du Colombier 	/*
276e79e25eaSDavid du Colombier 	 * Some servers (e.g., www.google.com) return 200 OK
277e79e25eaSDavid du Colombier 	 * when you ask for the entire page in one range.
278e79e25eaSDavid du Colombier 	 */
279e79e25eaSDavid du Colombier 	if(strstr(status, "206 Partial Content")==nil
280e79e25eaSDavid du Colombier 	&& (b->off!=0 || b->len!=size || strstr(status, "200 OK")==nil)){
281e79e25eaSDavid du Colombier 		free(status);
282e79e25eaSDavid du Colombier 		close(netfd);
283e79e25eaSDavid du Colombier 		werrstr("did not get requested range");
284e79e25eaSDavid du Colombier 		return nil;
285e79e25eaSDavid du Colombier 	}
286e79e25eaSDavid du Colombier 	free(status);
287e79e25eaSDavid du Colombier 
288e79e25eaSDavid du Colombier 	data = emalloc9p(b->len);
289e79e25eaSDavid du Colombier 	if(Bread(&netbio, data, b->len) != b->len){
290e79e25eaSDavid du Colombier 		free(data);
291e79e25eaSDavid du Colombier 		close(netfd);
292e79e25eaSDavid du Colombier 		werrstr("not enough bytes read");
293e79e25eaSDavid du Colombier 		return nil;
294e79e25eaSDavid du Colombier 	}
295e79e25eaSDavid du Colombier 
296e79e25eaSDavid du Colombier 	b->p = data;
297e79e25eaSDavid du Colombier 
298e79e25eaSDavid du Colombier 	close(netfd);
299e79e25eaSDavid du Colombier 	return data;
300e79e25eaSDavid du Colombier }
301e79e25eaSDavid du Colombier 
302e79e25eaSDavid du Colombier void
httpfilereadproc(void *)303e79e25eaSDavid du Colombier httpfilereadproc(void*)
304e79e25eaSDavid du Colombier {
305e79e25eaSDavid du Colombier 	Block *b;
306e79e25eaSDavid du Colombier 
307e79e25eaSDavid du Colombier 	threadsetname("httpfilereadproc");
308e79e25eaSDavid du Colombier 
309e79e25eaSDavid du Colombier 	for(;;){
310e79e25eaSDavid du Colombier 		b = recvp(httpchan);
311e79e25eaSDavid du Colombier 		if(b == nil)
312e79e25eaSDavid du Colombier 			continue;
313e79e25eaSDavid du Colombier 		if(getrange(b) == nil)
314e79e25eaSDavid du Colombier 			sysfatal("getrange: %r");
315e79e25eaSDavid du Colombier 		sendp(finishchan, b);
316e79e25eaSDavid du Colombier 	}
317e79e25eaSDavid du Colombier }
318e79e25eaSDavid du Colombier 
319e79e25eaSDavid du Colombier typedef struct Tab Tab;
320e79e25eaSDavid du Colombier struct Tab
321e79e25eaSDavid du Colombier {
322e79e25eaSDavid du Colombier 	char *name;
323e79e25eaSDavid du Colombier 	ulong mode;
324e79e25eaSDavid du Colombier };
325e79e25eaSDavid du Colombier 
326e79e25eaSDavid du Colombier Tab tab[] =
327e79e25eaSDavid du Colombier {
328e79e25eaSDavid du Colombier 	"/",		DMDIR|0555,
329e79e25eaSDavid du Colombier 	nil,		0444,
330e79e25eaSDavid du Colombier };
331e79e25eaSDavid du Colombier 
332e79e25eaSDavid du Colombier static void
fillstat(Dir * d,uvlong path)333e79e25eaSDavid du Colombier fillstat(Dir *d, uvlong path)
334e79e25eaSDavid du Colombier {
335e79e25eaSDavid du Colombier 	Tab *t;
336e79e25eaSDavid du Colombier 
337e79e25eaSDavid du Colombier 	memset(d, 0, sizeof(*d));
338e79e25eaSDavid du Colombier 	d->uid = estrdup9p(user);
339e79e25eaSDavid du Colombier 	d->gid = estrdup9p(user);
340e79e25eaSDavid du Colombier 	d->qid.path = path;
341e79e25eaSDavid du Colombier 	d->atime = d->mtime = time0;
342e79e25eaSDavid du Colombier 	t = &tab[TYPE(path)];
343e79e25eaSDavid du Colombier 	d->name = estrdup9p(t->name);
344e79e25eaSDavid du Colombier 	d->length = size;
345e79e25eaSDavid du Colombier 	d->qid.type = t->mode>>24;
346e79e25eaSDavid du Colombier 	d->mode = t->mode;
347e79e25eaSDavid du Colombier }
348e79e25eaSDavid du Colombier 
349e79e25eaSDavid du Colombier static void
fsattach(Req * r)350e79e25eaSDavid du Colombier fsattach(Req *r)
351e79e25eaSDavid du Colombier {
352e79e25eaSDavid du Colombier 	if(r->ifcall.aname && r->ifcall.aname[0]){
353e79e25eaSDavid du Colombier 		respond(r, "invalid attach specifier");
354e79e25eaSDavid du Colombier 		return;
355e79e25eaSDavid du Colombier 	}
356e79e25eaSDavid du Colombier 	r->fid->qid.path = PATH(Qroot, 0);
357e79e25eaSDavid du Colombier 	r->fid->qid.type = QTDIR;
358e79e25eaSDavid du Colombier 	r->fid->qid.vers = 0;
359e79e25eaSDavid du Colombier 	r->ofcall.qid = r->fid->qid;
360e79e25eaSDavid du Colombier 	respond(r, nil);
361e79e25eaSDavid du Colombier }
362e79e25eaSDavid du Colombier 
363e79e25eaSDavid du Colombier static void
fsstat(Req * r)364e79e25eaSDavid du Colombier fsstat(Req *r)
365e79e25eaSDavid du Colombier {
366e79e25eaSDavid du Colombier 	fillstat(&r->d, r->fid->qid.path);
367e79e25eaSDavid du Colombier 	respond(r, nil);
368e79e25eaSDavid du Colombier }
369e79e25eaSDavid du Colombier 
370e79e25eaSDavid du Colombier static int
rootgen(int i,Dir * d,void *)371e79e25eaSDavid du Colombier rootgen(int i, Dir *d, void*)
372e79e25eaSDavid du Colombier {
373e79e25eaSDavid du Colombier 	i += Qroot + 1;
374e79e25eaSDavid du Colombier 	if(i <= Qfile){
375e79e25eaSDavid du Colombier 		fillstat(d, i);
376e79e25eaSDavid du Colombier 		return 0;
377e79e25eaSDavid du Colombier 	}
378e79e25eaSDavid du Colombier 	return -1;
379e79e25eaSDavid du Colombier }
380e79e25eaSDavid du Colombier 
381e79e25eaSDavid du Colombier static char*
fswalk1(Fid * fid,char * name,Qid * qid)382e79e25eaSDavid du Colombier fswalk1(Fid *fid, char *name, Qid *qid)
383e79e25eaSDavid du Colombier {
384e79e25eaSDavid du Colombier 	int i;
385e79e25eaSDavid du Colombier 	ulong path;
386e79e25eaSDavid du Colombier 
387e79e25eaSDavid du Colombier 	path = fid->qid.path;
388e79e25eaSDavid du Colombier 	if(!(fid->qid.type & QTDIR))
389e79e25eaSDavid du Colombier 		return "walk in non-directory";
390e79e25eaSDavid du Colombier 
391e79e25eaSDavid du Colombier 	if(strcmp(name, "..") == 0){
392e79e25eaSDavid du Colombier 		switch(TYPE(path)){
393e79e25eaSDavid du Colombier 		case Qroot:
394e79e25eaSDavid du Colombier 			return nil;
395e79e25eaSDavid du Colombier 		default:
396e79e25eaSDavid du Colombier 			return "bug in fswalk1";
397e79e25eaSDavid du Colombier 		}
398e79e25eaSDavid du Colombier 	}
399e79e25eaSDavid du Colombier 
400e79e25eaSDavid du Colombier 	i = TYPE(path) + 1;
401e79e25eaSDavid du Colombier 	while(i < nelem(tab)){
402e79e25eaSDavid du Colombier 		if(strcmp(name, tab[i].name) == 0){
403e79e25eaSDavid du Colombier 			qid->path = PATH(i, NUM(path));
404e79e25eaSDavid du Colombier 			qid->type = tab[i].mode>>24;
405e79e25eaSDavid du Colombier 			return nil;
406e79e25eaSDavid du Colombier 		}
407e79e25eaSDavid du Colombier 		if(tab[i].mode & DMDIR)
408e79e25eaSDavid du Colombier 			break;
409e79e25eaSDavid du Colombier 		i++;
410e79e25eaSDavid du Colombier 	}
411e79e25eaSDavid du Colombier 	return "directory entry not found";
412e79e25eaSDavid du Colombier }
413e79e25eaSDavid du Colombier 
414e79e25eaSDavid du Colombier vlong
getfilesize(void)415e79e25eaSDavid du Colombier getfilesize(void)
416e79e25eaSDavid du Colombier {
417e79e25eaSDavid du Colombier 	char *status;
418e79e25eaSDavid du Colombier 	vlong size;
419e79e25eaSDavid du Colombier 	int netfd;
420e79e25eaSDavid du Colombier 	static Biobuf netbio;
421e79e25eaSDavid du Colombier 
422e79e25eaSDavid du Colombier 	netfd = dialhttp(&netbio);
423e79e25eaSDavid du Colombier 
424e79e25eaSDavid du Colombier 	fprint(netfd,
425e79e25eaSDavid du Colombier 		"HEAD %s HTTP/1.1\r\n"
426e79e25eaSDavid du Colombier 		"Host: %s\r\n"
427e79e25eaSDavid du Colombier 		"Accept-Encoding:\r\n"
428e79e25eaSDavid du Colombier 		"\r\n",
429e79e25eaSDavid du Colombier 		get, host);
430e79e25eaSDavid du Colombier 
431e79e25eaSDavid du Colombier 	status = readhttphdr(&netbio, &size);
432e79e25eaSDavid du Colombier 	if(strstr(status, "200 OK") == nil){
433e79e25eaSDavid du Colombier 		werrstr("%s", status);
434e79e25eaSDavid du Colombier 		size = -1;
435e79e25eaSDavid du Colombier 	}
436e79e25eaSDavid du Colombier 	free(status);
437e79e25eaSDavid du Colombier 
438e79e25eaSDavid du Colombier 	close(netfd);
439e79e25eaSDavid du Colombier 	return size;
440e79e25eaSDavid du Colombier }
441e79e25eaSDavid du Colombier 
442e79e25eaSDavid du Colombier void
fileread(Req * r)443e79e25eaSDavid du Colombier fileread(Req *r)
444e79e25eaSDavid du Colombier {
445e79e25eaSDavid du Colombier 	Block *b;
446e79e25eaSDavid du Colombier 
447e79e25eaSDavid du Colombier 	if(r->ifcall.offset > size){
448e79e25eaSDavid du Colombier 		respond(r, nil);
449e79e25eaSDavid du Colombier 		return;
450e79e25eaSDavid du Colombier 	}
451e79e25eaSDavid du Colombier 
452e79e25eaSDavid du Colombier 	if((b = findblock(&cache, r->ifcall.offset)) != nil){
453e79e25eaSDavid du Colombier 		readfrom(r, b);
454e79e25eaSDavid du Colombier 		return;
455e79e25eaSDavid du Colombier 	}
456e79e25eaSDavid du Colombier 	if((b = findblock(&inprogress, r->ifcall.offset)) == nil){
457e79e25eaSDavid du Colombier 		b = emalloc9p(sizeof(Block));
458e79e25eaSDavid du Colombier 		b->off = r->ifcall.offset - (r->ifcall.offset % Blocksize);
459e79e25eaSDavid du Colombier 		addblock(&inprogress, b);
460e79e25eaSDavid du Colombier 		if(inprogress.first == b)
461e79e25eaSDavid du Colombier 			sendp(httpchan, b);
462e79e25eaSDavid du Colombier 	}
463e79e25eaSDavid du Colombier 	queuereq(b, r);
464e79e25eaSDavid du Colombier }
465e79e25eaSDavid du Colombier 
466e79e25eaSDavid du Colombier static void
fsopen(Req * r)467e79e25eaSDavid du Colombier fsopen(Req *r)
468e79e25eaSDavid du Colombier {
469e79e25eaSDavid du Colombier 	if(r->ifcall.mode != OREAD){
470e79e25eaSDavid du Colombier 		respond(r, "permission denied");
471e79e25eaSDavid du Colombier 		return;
472e79e25eaSDavid du Colombier 	}
473e79e25eaSDavid du Colombier 	respond(r, nil);
474e79e25eaSDavid du Colombier }
475e79e25eaSDavid du Colombier 
476e79e25eaSDavid du Colombier void
finishthread(void *)477e79e25eaSDavid du Colombier finishthread(void*)
478e79e25eaSDavid du Colombier {
479e79e25eaSDavid du Colombier 	Block *b;
480e79e25eaSDavid du Colombier 	Req *r, *nextr;
481e79e25eaSDavid du Colombier 
482e79e25eaSDavid du Colombier 	threadsetname("finishthread");
483e79e25eaSDavid du Colombier 
484e79e25eaSDavid du Colombier 	for(;;){
485e79e25eaSDavid du Colombier 		b = recvp(finishchan);
486e79e25eaSDavid du Colombier 		assert(b == inprogress.first);
487e79e25eaSDavid du Colombier 		inprogress.first = b->link;
488e79e25eaSDavid du Colombier 		ncache++;
489e79e25eaSDavid du Colombier 		if(ncache >= mcache)
490e79e25eaSDavid du Colombier 			evictblock(&cache);
491e79e25eaSDavid du Colombier 		addblock(&cache, b);
492e79e25eaSDavid du Colombier 		for(r=b->rq; r; r=nextr){
493e79e25eaSDavid du Colombier 			nextr = r->aux;
494e79e25eaSDavid du Colombier 			readfrom(r, b);
495e79e25eaSDavid du Colombier 		}
496e79e25eaSDavid du Colombier 		b->rq = nil;
497e79e25eaSDavid du Colombier 		if(inprogress.first)
498e79e25eaSDavid du Colombier 			sendp(httpchan, inprogress.first);
499e79e25eaSDavid du Colombier 	}
500e79e25eaSDavid du Colombier }
501e79e25eaSDavid du Colombier 
502e79e25eaSDavid du Colombier void
fsnetproc(void *)503e79e25eaSDavid du Colombier fsnetproc(void*)
504e79e25eaSDavid du Colombier {
505e79e25eaSDavid du Colombier 	Req *r;
506e79e25eaSDavid du Colombier 	Block *b;
507e79e25eaSDavid du Colombier 
508e79e25eaSDavid du Colombier 	threadcreate(finishthread, nil, 8192);
509e79e25eaSDavid du Colombier 
510e79e25eaSDavid du Colombier 	threadsetname("fsnetproc");
511e79e25eaSDavid du Colombier 
512e79e25eaSDavid du Colombier 	for(;;){
513e79e25eaSDavid du Colombier 		r = recvp(reqchan);
514e79e25eaSDavid du Colombier 		switch(r->ifcall.type){
515e79e25eaSDavid du Colombier 		case Tflush:
516e79e25eaSDavid du Colombier 			b = findblock(&inprogress, r->ifcall.offset);
517e79e25eaSDavid du Colombier 			delreq(b, r->oldreq);
518e79e25eaSDavid du Colombier 			respond(r->oldreq, "interrupted");
519e79e25eaSDavid du Colombier 			respond(r, nil);
520e79e25eaSDavid du Colombier 			break;
521e79e25eaSDavid du Colombier 		case Tread:
522e79e25eaSDavid du Colombier 			fileread(r);
523e79e25eaSDavid du Colombier 			break;
524e79e25eaSDavid du Colombier 		default:
525e79e25eaSDavid du Colombier 			respond(r, "bug in fsthread");
526e79e25eaSDavid du Colombier 			break;
527e79e25eaSDavid du Colombier 		}
528e79e25eaSDavid du Colombier 	}
529e79e25eaSDavid du Colombier }
530e79e25eaSDavid du Colombier 
531e79e25eaSDavid du Colombier static void
fsflush(Req * r)532e79e25eaSDavid du Colombier fsflush(Req *r)
533e79e25eaSDavid du Colombier {
534e79e25eaSDavid du Colombier 	sendp(reqchan, r);
535e79e25eaSDavid du Colombier }
536e79e25eaSDavid du Colombier 
537e79e25eaSDavid du Colombier static void
fsread(Req * r)538e79e25eaSDavid du Colombier fsread(Req *r)
539e79e25eaSDavid du Colombier {
540e79e25eaSDavid du Colombier 	char e[ERRMAX];
541e79e25eaSDavid du Colombier 	ulong path;
542e79e25eaSDavid du Colombier 
543e79e25eaSDavid du Colombier 	path = r->fid->qid.path;
544e79e25eaSDavid du Colombier 	switch(TYPE(path)){
545e79e25eaSDavid du Colombier 	case Qroot:
546e79e25eaSDavid du Colombier 		dirread9p(r, rootgen, nil);
547e79e25eaSDavid du Colombier 		respond(r, nil);
548e79e25eaSDavid du Colombier 		break;
549e79e25eaSDavid du Colombier 	case Qfile:
550e79e25eaSDavid du Colombier 		sendp(reqchan, r);
551e79e25eaSDavid du Colombier 		break;
552e79e25eaSDavid du Colombier 	default:
553e79e25eaSDavid du Colombier 		snprint(e, sizeof(e), "bug in fsread path=%lux", path);
554e79e25eaSDavid du Colombier 		respond(r, e);
555e79e25eaSDavid du Colombier 		break;
556e79e25eaSDavid du Colombier 	}
557e79e25eaSDavid du Colombier }
558e79e25eaSDavid du Colombier 
559e79e25eaSDavid du Colombier Srv fs =
560e79e25eaSDavid du Colombier {
561e79e25eaSDavid du Colombier .attach=		fsattach,
562e79e25eaSDavid du Colombier .walk1=		fswalk1,
563e79e25eaSDavid du Colombier .open=		fsopen,
564e79e25eaSDavid du Colombier .read=		fsread,
565e79e25eaSDavid du Colombier .stat=		fsstat,
566e79e25eaSDavid du Colombier .flush=		fsflush,
567e79e25eaSDavid du Colombier .end=		hangupclient,
568e79e25eaSDavid du Colombier };
569e79e25eaSDavid du Colombier 
570e79e25eaSDavid du Colombier void
threadmain(int argc,char ** argv)571e79e25eaSDavid du Colombier threadmain(int argc, char **argv)
572e79e25eaSDavid du Colombier {
573e79e25eaSDavid du Colombier 	char *defport, *mtpt, *srvname, *p;
574e79e25eaSDavid du Colombier 
575e79e25eaSDavid du Colombier 	mtpt = nil;
576e79e25eaSDavid du Colombier 	srvname = nil;
577e79e25eaSDavid du Colombier 	ARGBEGIN{
578e79e25eaSDavid du Colombier 	case 'D':
579e79e25eaSDavid du Colombier 		chatty9p++;
580e79e25eaSDavid du Colombier 		break;
581e79e25eaSDavid du Colombier 	case 'd':
582e79e25eaSDavid du Colombier 		debug++;
583e79e25eaSDavid du Colombier 		break;
584e79e25eaSDavid du Colombier 	case 's':
585e79e25eaSDavid du Colombier 		srvname = EARGF(usage());
586e79e25eaSDavid du Colombier 		break;
587e79e25eaSDavid du Colombier 	case 'm':
588e79e25eaSDavid du Colombier 		mtpt = EARGF(usage());
589e79e25eaSDavid du Colombier 		break;
590e79e25eaSDavid du Colombier 	case 'c':
591e79e25eaSDavid du Colombier 		mcache = atoi(EARGF(usage()));
592e79e25eaSDavid du Colombier 		break;
593e79e25eaSDavid du Colombier 	case 'f':
594e79e25eaSDavid du Colombier 		file = EARGF(usage());
595e79e25eaSDavid du Colombier 		break;
596e79e25eaSDavid du Colombier 	case 'x':
597e79e25eaSDavid du Colombier 		net = smprint("%s/net", EARGF(usage()));
598e79e25eaSDavid du Colombier 		break;
599e79e25eaSDavid du Colombier 	default:
600e79e25eaSDavid du Colombier 		usage();
601e79e25eaSDavid du Colombier 	}ARGEND;
602e79e25eaSDavid du Colombier 
603e79e25eaSDavid du Colombier 	if(srvname == nil && mtpt == nil)
604e79e25eaSDavid du Colombier 		mtpt = ".";
605e79e25eaSDavid du Colombier 
606e79e25eaSDavid du Colombier 	if(argc < 1)
607e79e25eaSDavid du Colombier 		usage();
608e79e25eaSDavid du Colombier 	if(mcache <= 0)
609e79e25eaSDavid du Colombier 		mcache = 32;
610e79e25eaSDavid du Colombier 
611e79e25eaSDavid du Colombier 	time0 = time(0);
612e79e25eaSDavid du Colombier 	host = url = estrdup9p(argv[0]);
613e79e25eaSDavid du Colombier 
614e79e25eaSDavid du Colombier 	defport = nil;
615e79e25eaSDavid du Colombier 	if(!cistrncmp(url, "https://", 8)){
616e79e25eaSDavid du Colombier 		host += 8;
617e79e25eaSDavid du Colombier 		usetls = 1;
618e79e25eaSDavid du Colombier 		defport = "https";
619e79e25eaSDavid du Colombier 	}else if(!cistrncmp(url, "http://", 7)){
620e79e25eaSDavid du Colombier 		host += 7;
621e79e25eaSDavid du Colombier 		defport = "http";
622e79e25eaSDavid du Colombier 	}else
623e79e25eaSDavid du Colombier 		sysfatal("unsupported url: %s", url);
624e79e25eaSDavid du Colombier 
625e79e25eaSDavid du Colombier 	if((p = strchr(host, '/')) != nil){
626e79e25eaSDavid du Colombier 		get = estrdup9p(p);
627e79e25eaSDavid du Colombier 		*p = '\0';
628e79e25eaSDavid du Colombier 	}else
629e79e25eaSDavid du Colombier 		get = "/";
630e79e25eaSDavid du Colombier 
631e79e25eaSDavid du Colombier 	port = strchr(host, ':');
632e79e25eaSDavid du Colombier 	if(port != nil)
633e79e25eaSDavid du Colombier 		*port++ = '\0';
634e79e25eaSDavid du Colombier 	else
635e79e25eaSDavid du Colombier 		port = defport;
636e79e25eaSDavid du Colombier 
637e79e25eaSDavid du Colombier 	if(file == nil){
638e79e25eaSDavid du Colombier 		file = strrchr(get, '/')+1;
639e79e25eaSDavid du Colombier 		if(*file == 0)
640e79e25eaSDavid du Colombier 			file = "index";
641e79e25eaSDavid du Colombier 	}
642e79e25eaSDavid du Colombier 
643e79e25eaSDavid du Colombier 	tab[Qfile].name = file;
644e79e25eaSDavid du Colombier 	user = getuser();
645e79e25eaSDavid du Colombier 	size = getfilesize();
646e79e25eaSDavid du Colombier 	if(size < 0)
647e79e25eaSDavid du Colombier 		sysfatal("getfilesize: %r");
648e79e25eaSDavid du Colombier 
649e79e25eaSDavid du Colombier 	reqchan = chancreate(sizeof(Req*), 0);
650e79e25eaSDavid du Colombier 	httpchan = chancreate(sizeof(Block*), 0);
651e79e25eaSDavid du Colombier 	finishchan = chancreate(sizeof(Block*), 0);
652e79e25eaSDavid du Colombier 
653e79e25eaSDavid du Colombier 	procrfork(fsnetproc, nil, Stacksize, RFNAMEG|RFNOTEG);
654e79e25eaSDavid du Colombier 	procrfork(httpfilereadproc, nil, Stacksize, RFNAMEG|RFNOTEG);
655e79e25eaSDavid du Colombier 
656e79e25eaSDavid du Colombier 	threadpostmountsrv(&fs, srvname, mtpt, MBEFORE);
657e79e25eaSDavid du Colombier 	threadexits(0);
658e79e25eaSDavid du Colombier }
659