1 /* contributed by 20h@r-36.net, September 2005 */
36 fprint(2, "usage: httpfile [-Dd] [-c count] [-f file] [-m mtpt] [-s srvname] [-x net] url\n");
46 #define PATH(type, n) ((type)|((n)<<8))
47 #define TYPE(path) ((int)(path) & 0xFF)
48 #define NUM(path) ((uint)(path)>>8)
55 typedef struct Block Block;
67 typedef struct Blocklist Blocklist;
78 queuereq(Block *b, Req *r)
84 b->erq = (Req**)&r->aux;
88 addblock(Blocklist *l, Block *b)
91 print("adding: %p %lld\n", b, b->off);
102 delreq(Block *b, Req *r)
106 for(l = &b->rq; *l; l = (Req**)&(*l)->aux){
118 evictblock(Blocklist *cache)
120 Block **l, **oldest, *b;
122 if(cache->first == nil)
126 for(l=&cache->first; *l; l=&(*l)->link)
127 if(oldest == nil || (*oldest)->lastuse > (*l)->lastuse)
131 *oldest = (*oldest)->link;
140 findblock(Blocklist *s, vlong off)
144 for(b = s->first; b != nil; b = b->link){
145 if(b->off <= off && off < b->off + Blocksize){
147 print("found: %lld -> %lld\n", off, b->off);
148 b->lastuse = time(0);
157 readfrom(Req *r, Block *b)
161 b->lastuse = time(0);
164 d = r->ifcall.offset - b->off;
165 if(b->off + d + n > b->off + b->len)
168 print("Reading from: %p %d %d\n", b->p, d, n);
169 memmove(r->ofcall.data, b->p + d, n);
181 threadexitsall("done");
189 if((fd=tlsClient(fd, &conn)) < 0)
190 sysfatal("tlsclient: %r");
211 readhttphdr(Biobuf *netbio, vlong *size)
216 while((s = Brdstr(netbio, '\n', 1)) != nil && s[0] != '\r'
220 if(strncmp(s, "Content-Length: ", 16) == 0 && size != nil)
221 *size = atoll(s + 16);
231 dialhttp(Biobuf *netbio)
235 netfd = dial(netmkaddr(host, net, port), 0, 0, 0);
237 sysfatal("dial: %r");
239 netfd = dotls(netfd);
240 Binit(netbio, netfd, OREAD);
251 static Biobuf netbio;
254 if(b->off + b->len > size)
255 b->len = size - b->off;
258 print("getrange: %lld %lld\n", b->off, b->len);
260 netfd = dialhttp(&netbio);
263 "GET %s HTTP/1.1\r\n"
265 "Accept-Encoding:\r\n"
266 "Range: bytes=%lld-%lld\r\n"
268 get, host, b->off, b->off+b->len);
271 status = readhttphdr(&netbio, nil);
276 * Some servers (e.g., www.google.com) return 200 OK
277 * when you ask for the entire page in one range.
279 if(strstr(status, "206 Partial Content")==nil
280 && (b->off!=0 || b->len!=size || strstr(status, "200 OK")==nil)){
283 werrstr("did not get requested range");
288 data = emalloc9p(b->len);
289 if(Bread(&netbio, data, b->len) != b->len){
292 werrstr("not enough bytes read");
303 httpfilereadproc(void*)
307 threadsetname("httpfilereadproc");
313 if(getrange(b) == nil)
314 sysfatal("getrange: %r");
315 sendp(finishchan, b);
319 typedef struct Tab Tab;
333 fillstat(Dir *d, uvlong path)
337 memset(d, 0, sizeof(*d));
338 d->uid = estrdup9p(user);
339 d->gid = estrdup9p(user);
341 d->atime = d->mtime = time0;
342 t = &tab[TYPE(path)];
343 d->name = estrdup9p(t->name);
345 d->qid.type = t->mode>>24;
352 if(r->ifcall.aname && r->ifcall.aname[0]){
353 respond(r, "invalid attach specifier");
356 r->fid->qid.path = PATH(Qroot, 0);
357 r->fid->qid.type = QTDIR;
358 r->fid->qid.vers = 0;
359 r->ofcall.qid = r->fid->qid;
366 fillstat(&r->d, r->fid->qid.path);
371 rootgen(int i, Dir *d, void*)
382 fswalk1(Fid *fid, char *name, Qid *qid)
387 path = fid->qid.path;
388 if(!(fid->qid.type & QTDIR))
389 return "walk in non-directory";
391 if(strcmp(name, "..") == 0){
396 return "bug in fswalk1";
401 while(i < nelem(tab)){
402 if(strcmp(name, tab[i].name) == 0){
403 qid->path = PATH(i, NUM(path));
404 qid->type = tab[i].mode>>24;
407 if(tab[i].mode & DMDIR)
411 return "directory entry not found";
420 static Biobuf netbio;
422 netfd = dialhttp(&netbio);
425 "HEAD %s HTTP/1.1\r\n"
427 "Accept-Encoding:\r\n"
431 status = readhttphdr(&netbio, &size);
432 if(strstr(status, "200 OK") == nil){
433 werrstr("%s", status);
447 if(r->ifcall.offset > size){
452 if((b = findblock(&cache, r->ifcall.offset)) != nil){
456 if((b = findblock(&inprogress, r->ifcall.offset)) == nil){
457 b = emalloc9p(sizeof(Block));
458 b->off = r->ifcall.offset - (r->ifcall.offset % Blocksize);
459 addblock(&inprogress, b);
460 if(inprogress.first == b)
469 if(r->ifcall.mode != OREAD){
470 respond(r, "permission denied");
482 threadsetname("finishthread");
485 b = recvp(finishchan);
486 assert(b == inprogress.first);
487 inprogress.first = b->link;
492 for(r=b->rq; r; r=nextr){
498 sendp(httpchan, inprogress.first);
508 threadcreate(finishthread, nil, 8192);
510 threadsetname("fsnetproc");
514 switch(r->ifcall.type){
516 b = findblock(&inprogress, r->ifcall.offset);
517 delreq(b, r->oldreq);
518 respond(r->oldreq, "interrupted");
525 respond(r, "bug in fsthread");
543 path = r->fid->qid.path;
546 dirread9p(r, rootgen, nil);
553 snprint(e, sizeof(e), "bug in fsread path=%lux", path);
571 threadmain(int argc, char **argv)
573 char *defport, *mtpt, *srvname, *p;
585 srvname = EARGF(usage());
588 mtpt = EARGF(usage());
591 mcache = atoi(EARGF(usage()));
594 file = EARGF(usage());
597 net = smprint("%s/net", EARGF(usage()));
603 if(srvname == nil && mtpt == nil)
612 host = url = estrdup9p(argv[0]);
615 if(!cistrncmp(url, "https://", 8)){
619 }else if(!cistrncmp(url, "http://", 7)){
623 sysfatal("unsupported url: %s", url);
625 if((p = strchr(host, '/')) != nil){
631 port = strchr(host, ':');
638 file = strrchr(get, '/')+1;
643 tab[Qfile].name = file;
645 size = getfilesize();
647 sysfatal("getfilesize: %r");
649 reqchan = chancreate(sizeof(Req*), 0);
650 httpchan = chancreate(sizeof(Block*), 0);
651 finishchan = chancreate(sizeof(Block*), 0);
653 procrfork(fsnetproc, nil, Stacksize, RFNAMEG|RFNOTEG);
654 procrfork(httpfilereadproc, nil, Stacksize, RFNAMEG|RFNOTEG);
656 threadpostmountsrv(&fs, srvname, mtpt, MBEFORE);