123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658 |
- /* contributed by 20h@r-36.net, September 2005 */
- #include <u.h>
- #include <libc.h>
- #include <bio.h>
- #include <ndb.h>
- #include <thread.h>
- #include <fcall.h>
- #include <9p.h>
- #include <mp.h>
- #include <libsec.h>
- enum
- {
- Blocksize = 64*1024,
- Stacksize = 8192,
- };
- char *host;
- char *file;
- char *port;
- char *url;
- char *get;
- char *user;
- char *net = "net";
- vlong size;
- int usetls;
- int debug;
- int ncache;
- int mcache;
- void
- usage(void)
- {
- fprint(2, "usage: httpfile [-Dd] [-c count] [-f file] [-m mtpt] [-s srvname] [-x net] url\n");
- exits("usage");
- }
- enum
- {
- Qroot,
- Qfile,
- };
- #define PATH(type, n) ((type)|((n)<<8))
- #define TYPE(path) ((int)(path) & 0xFF)
- #define NUM(path) ((uint)(path)>>8)
- Channel *reqchan;
- Channel *httpchan;
- Channel *finishchan;
- ulong time0;
- typedef struct Block Block;
- struct Block
- {
- uchar *p;
- vlong off;
- vlong len;
- Block *link;
- long lastuse;
- Req *rq;
- Req **erq;
- };
- typedef struct Blocklist Blocklist;
- struct Blocklist
- {
- Block *first;
- Block **end;
- };
- Blocklist cache;
- Blocklist inprogress;
- void
- queuereq(Block *b, Req *r)
- {
- if(b->rq==nil)
- b->erq = &b->rq;
- *b->erq = r;
- r->aux = nil;
- b->erq = (Req**)&r->aux;
- }
- void
- addblock(Blocklist *l, Block *b)
- {
- if(debug)
- print("adding: %p %lld\n", b, b->off);
- if(l->first == nil)
- l->end = &l->first;
- *l->end = b;
- b->link = nil;
- l->end = &b->link;
- b->lastuse = time(0);
- }
- void
- delreq(Block *b, Req *r)
- {
- Req **l;
- for(l = &b->rq; *l; l = (Req**)&(*l)->aux){
- if(*l == r){
- *l = r->aux;
- if(*l == nil)
- b->erq = l;
- free(r);
- return;
- }
- }
- }
- void
- evictblock(Blocklist *cache)
- {
- Block **l, **oldest, *b;
- if(cache->first == nil)
- return;
- oldest = nil;
- for(l=&cache->first; *l; l=&(*l)->link)
- if(oldest == nil || (*oldest)->lastuse > (*l)->lastuse)
- oldest = l;
- b = *oldest;
- *oldest = (*oldest)->link;
- if(*oldest == nil)
- cache->end = oldest;
- free(b->p);
- free(b);
- ncache--;
- }
- Block *
- findblock(Blocklist *s, vlong off)
- {
- Block *b;
- for(b = s->first; b != nil; b = b->link){
- if(b->off <= off && off < b->off + Blocksize){
- if(debug)
- print("found: %lld -> %lld\n", off, b->off);
- b->lastuse = time(0);
- return b;
- }
- }
- return nil;
- }
- void
- readfrom(Req *r, Block *b)
- {
- int d, n;
- b->lastuse = time(0);
- n = r->ifcall.count;
- d = r->ifcall.offset - b->off;
- if(b->off + d + n > b->off + b->len)
- n = b->len - d;
- if(debug)
- print("Reading from: %p %d %d\n", b->p, d, n);
- memmove(r->ofcall.data, b->p + d, n);
- r->ofcall.count = n;
- respond(r, nil);
- }
- void
- hangupclient(Srv*)
- {
- if(debug)
- print("Hangup.\n");
- threadexitsall("done");
- }
- int
- dotls(int fd)
- {
- TLSconn conn;
- if((fd=tlsClient(fd, &conn)) < 0)
- sysfatal("tlsclient: %r");
- if(conn.cert != nil)
- free(conn.cert);
- return fd;
- }
- char*
- nocr(char *s)
- {
- char *r, *w;
- for(r=w=s; *r; r++)
- if(*r != '\r')
- *w++ = *r;
- *w = 0;
- return s;
- }
- char*
- readhttphdr(Biobuf *netbio, vlong *size)
- {
- char *s, *stat;
- stat = nil;
- while((s = Brdstr(netbio, '\n', 1)) != nil && s[0] != '\r'
- && s[0] != '\0'){
- if(stat == nil)
- stat = estrdup9p(s);
- if(strncmp(s, "Content-Length: ", 16) == 0 && size != nil)
- *size = atoll(s + 16);
- free(s);
- }
- if(stat)
- nocr(stat);
- return stat;
- }
- int
- dialhttp(Biobuf *netbio)
- {
- int netfd;
- netfd = dial(netmkaddr(host, net, port), 0, 0, 0);
- if(netfd < 0)
- sysfatal("dial: %r");
- if(usetls)
- netfd = dotls(netfd);
- Binit(netbio, netfd, OREAD);
- return netfd;
- }
- uchar*
- getrange(Block *b)
- {
- uchar *data;
- char *status;
- int netfd;
- static Biobuf netbio;
- b->len = Blocksize;
- if(b->off + b->len > size)
- b->len = size - b->off;
- if(debug)
- print("getrange: %lld %lld\n", b->off, b->len);
- netfd = dialhttp(&netbio);
- fprint(netfd,
- "GET %s HTTP/1.1\r\n"
- "Host: %s\r\n"
- "Accept-Encoding:\r\n"
- "Range: bytes=%lld-%lld\r\n"
- "\r\n",
- get, host, b->off, b->off+b->len);
- Bflush(&netbio);
- status = readhttphdr(&netbio, nil);
- if(status == nil)
- return nil;
- /*
- * Some servers (e.g., www.google.com) return 200 OK
- * when you ask for the entire page in one range.
- */
- if(strstr(status, "206 Partial Content")==nil
- && (b->off!=0 || b->len!=size || strstr(status, "200 OK")==nil)){
- free(status);
- close(netfd);
- werrstr("did not get requested range");
- return nil;
- }
- free(status);
- data = emalloc9p(b->len);
- if(Bread(&netbio, data, b->len) != b->len){
- free(data);
- close(netfd);
- werrstr("not enough bytes read");
- return nil;
- }
- b->p = data;
- close(netfd);
- return data;
- }
- void
- httpfilereadproc(void*)
- {
- Block *b;
- threadsetname("httpfilereadproc");
- for(;;){
- b = recvp(httpchan);
- if(b == nil)
- continue;
- if(getrange(b) == nil)
- sysfatal("getrange: %r");
- sendp(finishchan, b);
- }
- }
- typedef struct Tab Tab;
- struct Tab
- {
- char *name;
- ulong mode;
- };
- Tab tab[] =
- {
- "/", DMDIR|0555,
- nil, 0444,
- };
- static void
- fillstat(Dir *d, uvlong path)
- {
- Tab *t;
- memset(d, 0, sizeof(*d));
- d->uid = estrdup9p(user);
- d->gid = estrdup9p(user);
- d->qid.path = path;
- d->atime = d->mtime = time0;
- t = &tab[TYPE(path)];
- d->name = estrdup9p(t->name);
- d->length = size;
- d->qid.type = t->mode>>24;
- d->mode = t->mode;
- }
- static void
- fsattach(Req *r)
- {
- if(r->ifcall.aname && r->ifcall.aname[0]){
- respond(r, "invalid attach specifier");
- return;
- }
- r->fid->qid.path = PATH(Qroot, 0);
- r->fid->qid.type = QTDIR;
- r->fid->qid.vers = 0;
- r->ofcall.qid = r->fid->qid;
- respond(r, nil);
- }
- static void
- fsstat(Req *r)
- {
- fillstat(&r->d, r->fid->qid.path);
- respond(r, nil);
- }
- static int
- rootgen(int i, Dir *d, void*)
- {
- i += Qroot + 1;
- if(i <= Qfile){
- fillstat(d, i);
- return 0;
- }
- return -1;
- }
- static char*
- fswalk1(Fid *fid, char *name, Qid *qid)
- {
- int i;
- ulong path;
- path = fid->qid.path;
- if(!(fid->qid.type & QTDIR))
- return "walk in non-directory";
- if(strcmp(name, "..") == 0){
- switch(TYPE(path)){
- case Qroot:
- return nil;
- default:
- return "bug in fswalk1";
- }
- }
- i = TYPE(path) + 1;
- while(i < nelem(tab)){
- if(strcmp(name, tab[i].name) == 0){
- qid->path = PATH(i, NUM(path));
- qid->type = tab[i].mode>>24;
- return nil;
- }
- if(tab[i].mode & DMDIR)
- break;
- i++;
- }
- return "directory entry not found";
- }
- vlong
- getfilesize(void)
- {
- char *status;
- vlong size;
- int netfd;
- static Biobuf netbio;
- netfd = dialhttp(&netbio);
- fprint(netfd,
- "HEAD %s HTTP/1.1\r\n"
- "Host: %s\r\n"
- "Accept-Encoding:\r\n"
- "\r\n",
- get, host);
- status = readhttphdr(&netbio, &size);
- if(strstr(status, "200 OK") == nil){
- werrstr("%s", status);
- size = -1;
- }
- free(status);
- close(netfd);
- return size;
- }
- void
- fileread(Req *r)
- {
- Block *b;
- if(r->ifcall.offset > size){
- respond(r, nil);
- return;
- }
- if((b = findblock(&cache, r->ifcall.offset)) != nil){
- readfrom(r, b);
- return;
- }
- if((b = findblock(&inprogress, r->ifcall.offset)) == nil){
- b = emalloc9p(sizeof(Block));
- b->off = r->ifcall.offset - (r->ifcall.offset % Blocksize);
- addblock(&inprogress, b);
- if(inprogress.first == b)
- sendp(httpchan, b);
- }
- queuereq(b, r);
- }
- static void
- fsopen(Req *r)
- {
- if(r->ifcall.mode != OREAD){
- respond(r, "permission denied");
- return;
- }
- respond(r, nil);
- }
- void
- finishthread(void*)
- {
- Block *b;
- Req *r, *nextr;
- threadsetname("finishthread");
- for(;;){
- b = recvp(finishchan);
- assert(b == inprogress.first);
- inprogress.first = b->link;
- ncache++;
- if(ncache >= mcache)
- evictblock(&cache);
- addblock(&cache, b);
- for(r=b->rq; r; r=nextr){
- nextr = r->aux;
- readfrom(r, b);
- }
- b->rq = nil;
- if(inprogress.first)
- sendp(httpchan, inprogress.first);
- }
- }
- void
- fsnetproc(void*)
- {
- Req *r;
- Block *b;
- threadcreate(finishthread, nil, 8192);
- threadsetname("fsnetproc");
- for(;;){
- r = recvp(reqchan);
- switch(r->ifcall.type){
- case Tflush:
- b = findblock(&inprogress, r->ifcall.offset);
- delreq(b, r->oldreq);
- respond(r->oldreq, "interrupted");
- respond(r, nil);
- break;
- case Tread:
- fileread(r);
- break;
- default:
- respond(r, "bug in fsthread");
- break;
- }
- }
- }
- static void
- fsflush(Req *r)
- {
- sendp(reqchan, r);
- }
- static void
- fsread(Req *r)
- {
- char e[ERRMAX];
- ulong path;
- path = r->fid->qid.path;
- switch(TYPE(path)){
- case Qroot:
- dirread9p(r, rootgen, nil);
- respond(r, nil);
- break;
- case Qfile:
- sendp(reqchan, r);
- break;
- default:
- snprint(e, sizeof(e), "bug in fsread path=%lux", path);
- respond(r, e);
- break;
- }
- }
- Srv fs =
- {
- .attach= fsattach,
- .walk1= fswalk1,
- .open= fsopen,
- .read= fsread,
- .stat= fsstat,
- .flush= fsflush,
- .end= hangupclient,
- };
- void
- threadmain(int argc, char **argv)
- {
- char *defport, *mtpt, *srvname, *p;
- mtpt = nil;
- srvname = nil;
- ARGBEGIN{
- case 'D':
- chatty9p++;
- break;
- case 'd':
- debug++;
- break;
- case 's':
- srvname = EARGF(usage());
- break;
- case 'm':
- mtpt = EARGF(usage());
- break;
- case 'c':
- mcache = atoi(EARGF(usage()));
- break;
- case 'f':
- file = EARGF(usage());
- break;
- case 'x':
- net = smprint("%s/net", EARGF(usage()));
- break;
- default:
- usage();
- }ARGEND;
- if(srvname == nil && mtpt == nil)
- mtpt = ".";
- if(argc < 1)
- usage();
- if(mcache <= 0)
- mcache = 32;
- time0 = time(0);
- host = url = estrdup9p(argv[0]);
- defport = nil;
- if(!cistrncmp(url, "https://", 8)){
- host += 8;
- usetls = 1;
- defport = "https";
- }else if(!cistrncmp(url, "http://", 7)){
- host += 7;
- defport = "http";
- }else
- sysfatal("unsupported url: %s", url);
- if((p = strchr(host, '/')) != nil){
- get = estrdup9p(p);
- *p = '\0';
- }else
- get = "/";
- port = strchr(host, ':');
- if(port != nil)
- *port++ = '\0';
- else
- port = defport;
- if(file == nil){
- file = strrchr(get, '/')+1;
- if(*file == 0)
- file = "index";
- }
- tab[Qfile].name = file;
- user = getuser();
- size = getfilesize();
- if(size < 0)
- sysfatal("getfilesize: %r");
- reqchan = chancreate(sizeof(Req*), 0);
- httpchan = chancreate(sizeof(Block*), 0);
- finishchan = chancreate(sizeof(Block*), 0);
- procrfork(fsnetproc, nil, Stacksize, RFNAMEG|RFNOTEG);
- procrfork(httpfilereadproc, nil, Stacksize, RFNAMEG|RFNOTEG);
- threadpostmountsrv(&fs, srvname, mtpt, MBEFORE);
- threadexits(0);
- }
|