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