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 memset(&conn, 0, sizeof(conn));
190 if((fd=tlsClient(fd, &conn)) < 0)
191 sysfatal("tlsclient: %r");
193 free(conn.sessionID);
210 readhttphdr(Biobuf *netbio, vlong *size)
215 while((s = Brdstr(netbio, '\n', 1)) != nil && s[0] != '\r'
219 if(strncmp(s, "Content-Length: ", 16) == 0 && size != nil)
220 *size = atoll(s + 16);
230 dialhttp(Biobuf *netbio)
234 netfd = dial(netmkaddr(host, net, port), 0, 0, 0);
236 sysfatal("dial: %r");
238 netfd = dotls(netfd);
239 Binit(netbio, netfd, OREAD);
250 static Biobuf netbio;
253 if(b->off + b->len > size)
254 b->len = size - b->off;
257 print("getrange: %lld %lld\n", b->off, b->len);
259 netfd = dialhttp(&netbio);
262 "GET %s HTTP/1.1\r\n"
264 "Accept-Encoding:\r\n"
265 "Range: bytes=%lld-%lld\r\n"
267 get, host, b->off, b->off+b->len-1);
270 status = readhttphdr(&netbio, nil);
275 * Some servers (e.g., www.google.com) return 200 OK
276 * when you ask for the entire page in one range.
278 if(strstr(status, "206 Partial Content")==nil
279 && (b->off!=0 || b->len!=size || strstr(status, "200 OK")==nil)){
282 werrstr("did not get requested range");
287 data = emalloc9p(b->len);
288 if(Bread(&netbio, data, b->len) != b->len){
291 werrstr("not enough bytes read");
302 httpfilereadproc(void*)
306 threadsetname("httpfilereadproc");
312 if(getrange(b) == nil)
313 sysfatal("getrange: %r");
314 sendp(finishchan, b);
318 typedef struct Tab Tab;
332 fillstat(Dir *d, uvlong path)
336 memset(d, 0, sizeof(*d));
337 d->uid = estrdup9p(user);
338 d->gid = estrdup9p(user);
340 d->atime = d->mtime = time0;
341 t = &tab[TYPE(path)];
342 d->name = estrdup9p(t->name);
344 d->qid.type = t->mode>>24;
351 if(r->ifcall.aname && r->ifcall.aname[0]){
352 respond(r, "invalid attach specifier");
355 r->fid->qid.path = PATH(Qroot, 0);
356 r->fid->qid.type = QTDIR;
357 r->fid->qid.vers = 0;
358 r->ofcall.qid = r->fid->qid;
365 fillstat(&r->d, r->fid->qid.path);
370 rootgen(int i, Dir *d, void*)
381 fswalk1(Fid *fid, char *name, Qid *qid)
386 path = fid->qid.path;
387 if(!(fid->qid.type & QTDIR))
388 return "walk in non-directory";
390 if(strcmp(name, "..") == 0){
395 return "bug in fswalk1";
400 while(i < nelem(tab)){
401 if(strcmp(name, tab[i].name) == 0){
402 qid->path = PATH(i, NUM(path));
403 qid->type = tab[i].mode>>24;
406 if(tab[i].mode & DMDIR)
410 return "directory entry not found";
419 static Biobuf netbio;
421 netfd = dialhttp(&netbio);
424 "HEAD %s HTTP/1.1\r\n"
426 "Accept-Encoding:\r\n"
430 status = readhttphdr(&netbio, &size);
431 if(strstr(status, "200 OK") == nil){
432 werrstr("%s", status);
446 if(r->ifcall.offset > size){
451 if((b = findblock(&cache, r->ifcall.offset)) != nil){
455 if((b = findblock(&inprogress, r->ifcall.offset)) == nil){
456 b = emalloc9p(sizeof(Block));
457 b->off = r->ifcall.offset - (r->ifcall.offset % Blocksize);
458 addblock(&inprogress, b);
459 if(inprogress.first == b)
468 if(r->ifcall.mode != OREAD){
469 respond(r, "permission denied");
481 threadsetname("finishthread");
484 b = recvp(finishchan);
485 assert(b == inprogress.first);
486 inprogress.first = b->link;
491 for(r=b->rq; r; r=nextr){
497 sendp(httpchan, inprogress.first);
507 threadcreate(finishthread, nil, 8192);
509 threadsetname("fsnetproc");
513 switch(r->ifcall.type){
515 b = findblock(&inprogress, r->ifcall.offset);
516 delreq(b, r->oldreq);
517 respond(r->oldreq, "interrupted");
524 respond(r, "bug in fsthread");
542 path = r->fid->qid.path;
545 dirread9p(r, rootgen, nil);
552 snprint(e, sizeof(e), "bug in fsread path=%lux", path);
570 threadmain(int argc, char **argv)
572 char *defport, *mtpt, *srvname, *p;
584 srvname = EARGF(usage());
587 mtpt = EARGF(usage());
590 mcache = atoi(EARGF(usage()));
593 file = EARGF(usage());
596 net = smprint("%s/net", EARGF(usage()));
602 if(srvname == nil && mtpt == nil)
611 host = url = estrdup9p(argv[0]);
614 if(!cistrncmp(url, "https://", 8)){
618 }else if(!cistrncmp(url, "http://", 7)){
622 sysfatal("unsupported url: %s", url);
624 if((p = strchr(host, '/')) != nil){
630 port = strchr(host, ':');
637 file = strrchr(get, '/')+1;
642 tab[Qfile].name = file;
644 size = getfilesize();
646 sysfatal("getfilesize: %r");
648 reqchan = chancreate(sizeof(Req*), 0);
649 httpchan = chancreate(sizeof(Block*), 0);
650 finishchan = chancreate(sizeof(Block*), 0);
652 procrfork(fsnetproc, nil, Stacksize, RFNAMEG|RFNOTEG);
653 procrfork(httpfilereadproc, nil, Stacksize, RFNAMEG|RFNOTEG);
655 threadpostmountsrv(&fs, srvname, mtpt, MBEFORE);