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){
109 evictblock(Blocklist *cache)
111 Block **l, **oldest, *b;
114 for(l=&cache->first; (b=*l) != nil; l=&b->link){
115 if(b->rq != nil) /* dont touch block when still requests queued */
117 if(b->rq != nil && (oldest == nil || (*oldest)->lastuse > b->lastuse))
125 if((*oldest = b->link) == nil)
134 findblock(Blocklist *s, vlong off)
138 for(b = s->first; b != nil; b = b->link){
139 if(off >= b->off && off < b->off + b->len){
141 print("found: %lld -> %lld\n", off, b->off);
142 b->lastuse = time(0);
150 readfrom(Req *r, Block *b)
155 d = r->ifcall.offset - b->off;
159 print("Reading from: %p %d %d\n", b->p, d, n);
160 memmove(r->ofcall.data, b->p + d, n);
172 threadexitsall("done");
176 readfile(int fd, char *buf, int nbuf)
180 for(n = 0; n < nbuf; n += r)
181 if((r = read(fd, buf + n, nbuf - n)) <= 0)
187 readstring(int fd, char *buf, int nbuf)
191 if((n = readfile(fd, buf, nbuf-1)) < 0){
195 if(n > 0 && buf[n-1] == '\n')
209 if(b->off + b->len > size)
210 b->len = size - b->off;
213 print("getrange: %lld %lld\n", b->off, b->len);
215 if(fprint(webctlfd, "url %s\n", url) < 0)
217 if(fprint(webctlfd, "request GET\n") < 0)
219 if(fprint(webctlfd, "headers Range: bytes=%lld-%lld\n", b->off, b->off+b->len-1) < 0)
222 /* start the request */
223 snprint(buf, sizeof(buf), "%s/body", webconn);
224 if((fd = open(buf, OREAD)) < 0)
227 /* verify content-range response header */
228 snprint(buf, sizeof(buf), "%s/contentrange", webconn);
229 if((cfd = open(buf, OREAD)) < 0){
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 if(readfile(fd, (char*)data, b->len) != b->len){
258 httpfilereadproc(void*)
262 threadsetname("httpfilereadproc %s", url);
268 if(getrange(b) == nil)
269 sysfatal("getrange: %r");
270 sendp(finishchan, b);
274 typedef struct Tab Tab;
288 fillstat(Dir *d, uvlong path)
292 memset(d, 0, sizeof(*d));
293 d->uid = estrdup9p(user);
294 d->gid = estrdup9p(user);
296 d->atime = d->mtime = time0;
297 t = &tab[TYPE(path)];
298 d->name = estrdup9p(t->name);
300 d->qid.type = t->mode>>24;
307 if(r->ifcall.aname && r->ifcall.aname[0]){
308 respond(r, "invalid attach specifier");
311 r->fid->qid.path = PATH(Qroot, 0);
312 r->fid->qid.type = QTDIR;
313 r->fid->qid.vers = 0;
314 r->ofcall.qid = r->fid->qid;
321 fillstat(&r->d, r->fid->qid.path);
326 rootgen(int i, Dir *d, void*)
337 fswalk1(Fid *fid, char *name, Qid *qid)
342 path = fid->qid.path;
343 if(!(fid->qid.type & QTDIR))
344 return "walk in non-directory";
346 if(strcmp(name, "..") == 0){
351 return "bug in fswalk1";
356 while(i < nelem(tab)){
357 if(strcmp(name, tab[i].name) == 0){
358 qid->path = PATH(i, NUM(path));
359 qid->type = tab[i].mode>>24;
362 if(tab[i].mode & DMDIR)
366 return "directory entry not found";
375 if(fprint(webctlfd, "url %s\n", url) < 0)
377 if(fprint(webctlfd, "request HEAD\n") < 0)
379 snprint(buf, sizeof(buf), "%s/body", webconn);
380 if((fd = open(buf, OREAD)) < 0)
382 snprint(buf, sizeof(buf), "%s/contentlength", webconn);
383 cfd = open(buf, OREAD);
387 if(readstring(cfd, buf, sizeof(buf)) <= 0){
392 return strtoll(buf, nil, 10);
400 if(r->ifcall.offset >= size){
406 if((b = findblock(&cache, r->ifcall.offset)) != nil){
410 if((b = findblock(&inprogress, r->ifcall.offset)) == nil){
411 b = emalloc9p(sizeof(Block));
412 b->off = r->ifcall.offset - (r->ifcall.offset % Blocksize);
413 addblock(&inprogress, b);
414 if(inprogress.first == b)
423 if(r->ifcall.mode != OREAD){
424 respond(r, "permission denied");
436 threadsetname("finishthread");
439 b = recvp(finishchan);
440 assert(b == inprogress.first);
441 inprogress.first = b->link;
446 while((r = b->rq) != nil){
452 sendp(httpchan, inprogress.first);
462 threadcreate(finishthread, nil, 8192);
464 threadsetname("fsnetproc");
468 switch(r->ifcall.type){
471 b = findblock(&inprogress, o->ifcall.offset);
474 respond(o, "interrupted");
481 respond(r, "bug in fsthread");
499 path = r->fid->qid.path;
502 dirread9p(r, rootgen, nil);
509 snprint(e, sizeof(e), "bug in fsread path=%lux", path);
527 threadmain(int argc, char **argv)
529 char *mtpt, *srvname, *p;
541 srvname = EARGF(usage());
544 mtpt = EARGF(usage());
547 mcache = atoi(EARGF(usage()));
550 file = EARGF(usage());
556 if(srvname == nil && mtpt == nil)
565 url = estrdup9p(argv[0]);
567 file = strrchr(url, '/');
568 if(file == nil || file[1] == '\0')
574 snprint(webconn, sizeof(webconn), "/mnt/web/clone");
575 if((webctlfd = open(webconn, ORDWR)) < 0)
576 sysfatal("open: %r");
577 p = strrchr(webconn, '/')+1;
578 if(readstring(webctlfd, p, webconn+sizeof(webconn)-p) <= 0)
579 sysfatal("read: %r");
581 tab[Qfile].name = file;
583 size = getfilesize();
585 sysfatal("getfilesize: %r");
587 reqchan = chancreate(sizeof(Req*), 0);
588 httpchan = chancreate(sizeof(Block*), 0);
589 finishchan = chancreate(sizeof(Block*), 0);
591 procrfork(fsnetproc, nil, Stacksize, RFNAMEG|RFNOTEG);
592 procrfork(httpfilereadproc, nil, Stacksize, RFNAMEG|RFNOTEG);
594 threadpostmountsrv(&fs, srvname, mtpt, MBEFORE);