1 /* contributed by 20h@r-36.net, September 2005 */
15 char *user, *url, *file;
28 fprint(2, "usage: httpfile [-Dd] [-c count] [-f file] [-m mtpt] [-s srvname] url\n");
38 #define PATH(type, n) ((type)|((n)<<8))
39 #define TYPE(path) ((int)(path) & 0xFF)
40 #define NUM(path) ((uint)(path)>>8)
47 typedef struct Block Block;
59 typedef struct Blocklist Blocklist;
70 queuereq(Block *b, Req *r)
76 b->erq = (Req**)&r->aux;
80 addblock(Blocklist *l, Block *b)
83 print("adding: %p %lld\n", b, b->off);
94 delreq(Block *b, Req *r)
98 for(l = &b->rq; *l; l = (Req**)&(*l)->aux){
104 respond(r, "interrupted");
111 evictblock(Blocklist *cache)
113 Block **l, **oldest, *b;
116 for(l=&cache->first; (b=*l) != nil; l=&b->link){
117 if(b->rq != nil) /* dont touch block when still requests queued */
119 if(oldest == nil || (*oldest)->lastuse > b->lastuse)
123 if(oldest == nil || *oldest == nil || (*oldest)->rq != nil)
127 if((*oldest = b->link) == nil)
136 findblock(Blocklist *s, vlong off)
140 for(b = s->first; b != nil; b = b->link){
141 if(off >= b->off && off < b->off + b->len){
143 print("found: %lld -> %lld\n", off, b->off);
144 b->lastuse = time(0);
152 readfrom(Req *r, Block *b)
157 d = r->ifcall.offset - b->off;
161 print("Reading from: %p %d %d\n", b->p, d, n);
162 memmove(r->ofcall.data, b->p + d, n);
174 threadexitsall("done");
178 readfile(int fd, char *buf, int nbuf)
182 for(n = 0; n < nbuf; n += r)
183 if((r = read(fd, buf + n, nbuf - n)) <= 0)
189 readstring(int fd, char *buf, int nbuf)
193 if((n = readfile(fd, buf, nbuf-1)) < 0){
197 if(n > 0 && buf[n-1] == '\n')
211 print("getrange: %lld %lld\n", b->off, b->len);
213 if(fprint(webctlfd, "url %s\n", url) < 0)
215 if(fprint(webctlfd, "request GET\n") < 0)
217 if(fprint(webctlfd, "headers Range: bytes=%lld-%lld\n", b->off, b->off+b->len-1) < 0)
220 /* start the request */
221 snprint(buf, sizeof(buf), "%s/body", webconn);
222 if((fd = open(buf, OREAD)) < 0)
225 /* verify content-range response header */
226 snprint(buf, sizeof(buf), "%s/contentrange", webconn);
227 if((cfd = open(buf, OREAD)) < 0){
232 werrstr("bad contentrange header");
233 if(readstring(cfd, buf, sizeof(buf)) <= 0){
239 if(cistrncmp(buf, "bytes ", 6) != 0)
241 if(strtoll(buf + 6, nil, 10) != b->off)
246 data = emalloc9p(b->len);
247 werrstr("body data truncated");
248 if(readfile(fd, (char*)data, b->len) != b->len){
259 httpfilereadproc(void*)
263 threadsetname("httpfilereadproc %s", url);
269 if(getrange(b) == nil)
270 sysfatal("getrange: %r");
271 sendp(finishchan, b);
275 typedef struct Tab Tab;
289 fillstat(Dir *d, uvlong path)
293 memset(d, 0, sizeof(*d));
294 d->uid = estrdup9p(user);
295 d->gid = estrdup9p(user);
297 d->atime = d->mtime = time0;
298 t = &tab[TYPE(path)];
299 d->name = estrdup9p(t->name);
301 d->qid.type = t->mode>>24;
308 if(r->ifcall.aname && r->ifcall.aname[0]){
309 respond(r, "invalid attach specifier");
312 r->fid->qid.path = PATH(Qroot, 0);
313 r->fid->qid.type = QTDIR;
314 r->fid->qid.vers = 0;
315 r->ofcall.qid = r->fid->qid;
322 fillstat(&r->d, r->fid->qid.path);
327 rootgen(int i, Dir *d, void*)
338 fswalk1(Fid *fid, char *name, Qid *qid)
343 path = fid->qid.path;
344 if(!(fid->qid.type & QTDIR))
345 return "walk in non-directory";
347 if(strcmp(name, "..") == 0){
352 return "bug in fswalk1";
357 while(i < nelem(tab)){
358 if(strcmp(name, tab[i].name) == 0){
359 qid->path = PATH(i, NUM(path));
360 qid->type = tab[i].mode>>24;
363 if(tab[i].mode & DMDIR)
367 return "directory entry not found";
376 if(fprint(webctlfd, "url %s\n", url) < 0)
378 if(fprint(webctlfd, "request HEAD\n") < 0)
380 snprint(buf, sizeof(buf), "%s/body", webconn);
381 if((fd = open(buf, OREAD)) < 0)
383 snprint(buf, sizeof(buf), "%s/contentlength", webconn);
384 cfd = open(buf, OREAD);
388 if(readstring(cfd, buf, sizeof(buf)) <= 0){
393 return strtoll(buf, nil, 10);
401 if(r->ifcall.offset >= size){
407 if((b = findblock(&cache, r->ifcall.offset)) != nil){
411 if((b = findblock(&inprogress, r->ifcall.offset)) == nil){
412 b = emalloc9p(sizeof(Block));
413 b->off = r->ifcall.offset - (r->ifcall.offset % Blocksize);
415 if(b->off + b->len > size)
416 b->len = size - b->off;
417 addblock(&inprogress, b);
418 if(inprogress.first == b)
427 if(r->ifcall.mode != OREAD){
428 respond(r, "permission denied");
440 threadsetname("finishthread");
443 b = recvp(finishchan);
444 assert(b == inprogress.first);
445 inprogress.first = b->link;
446 if(++ncache >= mcache)
449 while((r = b->rq) != nil){
454 if(inprogress.first != nil)
455 sendp(httpchan, inprogress.first);
465 threadcreate(finishthread, nil, 8192);
467 threadsetname("fsnetproc");
471 switch(r->ifcall.type){
474 if(o->ifcall.type == Tread){
475 b = findblock(&inprogress, o->ifcall.offset);
485 respond(r, "bug in fsthread");
503 path = r->fid->qid.path;
506 dirread9p(r, rootgen, nil);
513 snprint(e, sizeof(e), "bug in fsread path=%lux", path);
531 threadmain(int argc, char **argv)
533 char *mtpt, *srvname, *p;
545 srvname = EARGF(usage());
548 mtpt = EARGF(usage());
551 mcache = atoi(EARGF(usage()));
554 file = EARGF(usage());
560 if(srvname == nil && mtpt == nil)
569 url = estrdup9p(argv[0]);
571 file = strrchr(url, '/');
572 if(file == nil || file[1] == '\0')
578 snprint(webconn, sizeof(webconn), "/mnt/web/clone");
579 if((webctlfd = open(webconn, ORDWR)) < 0)
580 sysfatal("open: %r");
581 p = strrchr(webconn, '/')+1;
582 if(readstring(webctlfd, p, webconn+sizeof(webconn)-p) <= 0)
583 sysfatal("read: %r");
585 tab[Qfile].name = file;
587 size = getfilesize();
589 sysfatal("getfilesize: %r");
591 reqchan = chancreate(sizeof(Req*), 0);
592 httpchan = chancreate(sizeof(Block*), 0);
593 finishchan = chancreate(sizeof(Block*), 0);
595 procrfork(fsnetproc, nil, Stacksize, RFNAMEG|RFNOTEG);
596 procrfork(httpfilereadproc, nil, Stacksize, RFNAMEG|RFNOTEG);
598 threadpostmountsrv(&fs, srvname, mtpt, MBEFORE);