httpfile: use webfs, fix 9p flushes
we can improve performance alot by using webfs which does http keep alives for us, so connection setup overhead is eleminated. fix 9p flushes and double frees.
This commit is contained in:
parent
5540ca6b68
commit
c79dc3263e
2 changed files with 115 additions and 186 deletions
|
@ -22,10 +22,6 @@ httpfile \- serve a single web file
|
||||||
.B -s
|
.B -s
|
||||||
.I srvname
|
.I srvname
|
||||||
]
|
]
|
||||||
[
|
|
||||||
.B -x
|
|
||||||
.I net
|
|
||||||
]
|
|
||||||
.I url
|
.I url
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.I Httpfile
|
.I Httpfile
|
||||||
|
@ -63,21 +59,15 @@ to post the 9P service as
|
||||||
and disables the default mount.
|
and disables the default mount.
|
||||||
.PP
|
.PP
|
||||||
The
|
The
|
||||||
.B -x
|
|
||||||
option specifies an alternate network directory
|
|
||||||
.RI ( e.g.,
|
|
||||||
.BR /net.alt ).
|
|
||||||
.PP
|
|
||||||
The
|
|
||||||
.B -c
|
.B -c
|
||||||
option sets the number of file blocks kept cached in memory (default 32).
|
option sets the number of file blocks kept cached in memory (default 32).
|
||||||
.SH EXAMPLE
|
.SH EXAMPLE
|
||||||
Mount an ISO image on a web server:
|
Mount an ISO image on a web server:
|
||||||
.IP
|
.IP
|
||||||
.EX
|
.EX
|
||||||
ip/httpfile http://www.9grid.de/plan9/plan9.iso
|
ip/httpfile http://www.r-36.net/9front/9front.iso
|
||||||
9660srv
|
9660srv
|
||||||
mount /srv/9660 /n/iso plan9.iso
|
mount /srv/9660 /n/iso 9front.iso
|
||||||
.EE
|
.EE
|
||||||
.SH SOURCE
|
.SH SOURCE
|
||||||
.B /sys/src/cmd/ip/httpfile.c
|
.B /sys/src/cmd/ip/httpfile.c
|
||||||
|
|
|
@ -2,13 +2,9 @@
|
||||||
|
|
||||||
#include <u.h>
|
#include <u.h>
|
||||||
#include <libc.h>
|
#include <libc.h>
|
||||||
#include <bio.h>
|
|
||||||
#include <ndb.h>
|
|
||||||
#include <thread.h>
|
#include <thread.h>
|
||||||
#include <fcall.h>
|
#include <fcall.h>
|
||||||
#include <9p.h>
|
#include <9p.h>
|
||||||
#include <mp.h>
|
|
||||||
#include <libsec.h>
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -16,13 +12,9 @@ enum
|
||||||
Stacksize = 8192,
|
Stacksize = 8192,
|
||||||
};
|
};
|
||||||
|
|
||||||
char *host;
|
char *user, *url, *file;
|
||||||
char *file;
|
char webconn[64];
|
||||||
char *port;
|
int webctlfd = -1;
|
||||||
char *url;
|
|
||||||
char *get;
|
|
||||||
char *user;
|
|
||||||
char *net = "net";
|
|
||||||
|
|
||||||
vlong size;
|
vlong size;
|
||||||
int usetls;
|
int usetls;
|
||||||
|
@ -33,7 +25,7 @@ int mcache;
|
||||||
void
|
void
|
||||||
usage(void)
|
usage(void)
|
||||||
{
|
{
|
||||||
fprint(2, "usage: httpfile [-Dd] [-c count] [-f file] [-m mtpt] [-s srvname] [-x net] url\n");
|
fprint(2, "usage: httpfile [-Dd] [-c count] [-f file] [-m mtpt] [-s srvname] url\n");
|
||||||
exits("usage");
|
exits("usage");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,10 +84,10 @@ addblock(Blocklist *l, Block *b)
|
||||||
|
|
||||||
if(l->first == nil)
|
if(l->first == nil)
|
||||||
l->end = &l->first;
|
l->end = &l->first;
|
||||||
*l->end = b;
|
|
||||||
b->link = nil;
|
|
||||||
l->end = &b->link;
|
|
||||||
b->lastuse = time(0);
|
b->lastuse = time(0);
|
||||||
|
b->link = nil;
|
||||||
|
*l->end = b;
|
||||||
|
l->end = &b->link;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -108,7 +100,6 @@ delreq(Block *b, Req *r)
|
||||||
*l = r->aux;
|
*l = r->aux;
|
||||||
if(*l == nil)
|
if(*l == nil)
|
||||||
b->erq = l;
|
b->erq = l;
|
||||||
free(r);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,18 +110,21 @@ evictblock(Blocklist *cache)
|
||||||
{
|
{
|
||||||
Block **l, **oldest, *b;
|
Block **l, **oldest, *b;
|
||||||
|
|
||||||
if(cache->first == nil)
|
oldest = nil;
|
||||||
|
for(l=&cache->first; (b=*l) != nil; l=&b->link){
|
||||||
|
if(b->rq != nil) /* dont touch block when still requests queued */
|
||||||
|
continue;
|
||||||
|
if(b->rq != nil && (oldest == nil || (*oldest)->lastuse > b->lastuse))
|
||||||
|
oldest = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(oldest == nil)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
oldest = nil;
|
|
||||||
for(l=&cache->first; *l; l=&(*l)->link)
|
|
||||||
if(oldest == nil || (*oldest)->lastuse > (*l)->lastuse)
|
|
||||||
oldest = l;
|
|
||||||
|
|
||||||
b = *oldest;
|
b = *oldest;
|
||||||
*oldest = (*oldest)->link;
|
if((*oldest = b->link) == nil)
|
||||||
if(*oldest == nil)
|
|
||||||
cache->end = oldest;
|
cache->end = oldest;
|
||||||
|
|
||||||
free(b->p);
|
free(b->p);
|
||||||
free(b);
|
free(b);
|
||||||
ncache--;
|
ncache--;
|
||||||
|
@ -142,14 +136,13 @@ findblock(Blocklist *s, vlong off)
|
||||||
Block *b;
|
Block *b;
|
||||||
|
|
||||||
for(b = s->first; b != nil; b = b->link){
|
for(b = s->first; b != nil; b = b->link){
|
||||||
if(b->off <= off && off < b->off + Blocksize){
|
if(off >= b->off && off < b->off + b->len){
|
||||||
if(debug)
|
if(debug)
|
||||||
print("found: %lld -> %lld\n", off, b->off);
|
print("found: %lld -> %lld\n", off, b->off);
|
||||||
b->lastuse = time(0);
|
b->lastuse = time(0);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,11 +151,9 @@ readfrom(Req *r, Block *b)
|
||||||
{
|
{
|
||||||
int d, n;
|
int d, n;
|
||||||
|
|
||||||
b->lastuse = time(0);
|
|
||||||
|
|
||||||
n = r->ifcall.count;
|
n = r->ifcall.count;
|
||||||
d = r->ifcall.offset - b->off;
|
d = r->ifcall.offset - b->off;
|
||||||
if(b->off + d + n > b->off + b->len)
|
if(d + n > b->len)
|
||||||
n = b->len - d;
|
n = b->len - d;
|
||||||
if(debug)
|
if(debug)
|
||||||
print("Reading from: %p %d %d\n", b->p, d, n);
|
print("Reading from: %p %d %d\n", b->p, d, n);
|
||||||
|
@ -181,73 +172,38 @@ hangupclient(Srv*)
|
||||||
threadexitsall("done");
|
threadexitsall("done");
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
dotls(int fd)
|
readfile(int fd, char *buf, int nbuf)
|
||||||
{
|
{
|
||||||
TLSconn conn;
|
int r, n;
|
||||||
|
|
||||||
memset(&conn, 0, sizeof(conn));
|
for(n = 0; n < nbuf; n += r)
|
||||||
if((fd=tlsClient(fd, &conn)) < 0)
|
if((r = read(fd, buf + n, nbuf - n)) <= 0)
|
||||||
sysfatal("tlsclient: %r");
|
break;
|
||||||
free(conn.cert);
|
return n;
|
||||||
free(conn.sessionID);
|
|
||||||
return fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char*
|
static int
|
||||||
nocr(char *s)
|
readstring(int fd, char *buf, int nbuf)
|
||||||
{
|
{
|
||||||
char *r, *w;
|
int n;
|
||||||
|
|
||||||
for(r=w=s; *r; r++)
|
if((n = readfile(fd, buf, nbuf-1)) < 0){
|
||||||
if(*r != '\r')
|
buf[0] = '\0';
|
||||||
*w++ = *r;
|
return -1;
|
||||||
*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)
|
if(n > 0 && buf[n-1] == '\n')
|
||||||
nocr(stat);
|
n--;
|
||||||
|
buf[n] = '\0';
|
||||||
return stat;
|
return n;
|
||||||
}
|
|
||||||
|
|
||||||
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*
|
uchar*
|
||||||
getrange(Block *b)
|
getrange(Block *b)
|
||||||
{
|
{
|
||||||
|
char buf[128];
|
||||||
|
int fd, cfd;
|
||||||
uchar *data;
|
uchar *data;
|
||||||
char *status;
|
|
||||||
int netfd;
|
|
||||||
static Biobuf netbio;
|
|
||||||
|
|
||||||
b->len = Blocksize;
|
b->len = Blocksize;
|
||||||
if(b->off + b->len > size)
|
if(b->off + b->len > size)
|
||||||
|
@ -256,45 +212,45 @@ getrange(Block *b)
|
||||||
if(debug)
|
if(debug)
|
||||||
print("getrange: %lld %lld\n", b->off, b->len);
|
print("getrange: %lld %lld\n", b->off, b->len);
|
||||||
|
|
||||||
netfd = dialhttp(&netbio);
|
if(fprint(webctlfd, "url %s\n", url) < 0)
|
||||||
|
return nil;
|
||||||
fprint(netfd,
|
if(fprint(webctlfd, "request GET\n") < 0)
|
||||||
"GET %s HTTP/1.1\r\n"
|
return nil;
|
||||||
"Host: %s\r\n"
|
if(fprint(webctlfd, "headers Range: bytes=%lld-%lld\n", b->off, b->off+b->len-1) < 0)
|
||||||
"Accept-Encoding:\r\n"
|
|
||||||
"Range: bytes=%lld-%lld\r\n"
|
|
||||||
"\r\n",
|
|
||||||
get, host, b->off, b->off+b->len-1);
|
|
||||||
Bflush(&netbio);
|
|
||||||
|
|
||||||
status = readhttphdr(&netbio, nil);
|
|
||||||
if(status == nil)
|
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
/*
|
/* start the request */
|
||||||
* Some servers (e.g., www.google.com) return 200 OK
|
snprint(buf, sizeof(buf), "%s/body", webconn);
|
||||||
* when you ask for the entire page in one range.
|
if((fd = open(buf, OREAD)) < 0)
|
||||||
*/
|
return nil;
|
||||||
if(strstr(status, "206 Partial Content")==nil
|
|
||||||
&& (b->off!=0 || b->len!=size || strstr(status, "200 OK")==nil)){
|
/* verify content-range response header */
|
||||||
free(status);
|
snprint(buf, sizeof(buf), "%s/contentrange", webconn);
|
||||||
close(netfd);
|
if((cfd = open(buf, OREAD)) < 0){
|
||||||
werrstr("did not get requested range");
|
close(fd);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
free(status);
|
if(readstring(cfd, buf, sizeof(buf)) <= 0){
|
||||||
|
Badrange:
|
||||||
|
close(cfd);
|
||||||
|
close(fd);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
if(cistrncmp(buf, "bytes ", 6) != 0)
|
||||||
|
goto Badrange;
|
||||||
|
if(strtoll(buf + 6, nil, 10) != b->off)
|
||||||
|
goto Badrange;
|
||||||
|
close(cfd);
|
||||||
|
|
||||||
|
/* read body data */
|
||||||
data = emalloc9p(b->len);
|
data = emalloc9p(b->len);
|
||||||
if(Bread(&netbio, data, b->len) != b->len){
|
if(readfile(fd, (char*)data, b->len) != b->len){
|
||||||
|
close(fd);
|
||||||
free(data);
|
free(data);
|
||||||
close(netfd);
|
|
||||||
werrstr("not enough bytes read");
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
close(fd);
|
||||||
b->p = data;
|
b->p = data;
|
||||||
|
|
||||||
close(netfd);
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +259,7 @@ httpfilereadproc(void*)
|
||||||
{
|
{
|
||||||
Block *b;
|
Block *b;
|
||||||
|
|
||||||
threadsetname("httpfilereadproc");
|
threadsetname("httpfilereadproc %s", url);
|
||||||
|
|
||||||
for(;;){
|
for(;;){
|
||||||
b = recvp(httpchan);
|
b = recvp(httpchan);
|
||||||
|
@ -413,29 +369,27 @@ fswalk1(Fid *fid, char *name, Qid *qid)
|
||||||
vlong
|
vlong
|
||||||
getfilesize(void)
|
getfilesize(void)
|
||||||
{
|
{
|
||||||
char *status;
|
char buf[128];
|
||||||
vlong size;
|
int fd, cfd;
|
||||||
int netfd;
|
|
||||||
static Biobuf netbio;
|
|
||||||
|
|
||||||
netfd = dialhttp(&netbio);
|
if(fprint(webctlfd, "url %s\n", url) < 0)
|
||||||
|
return -1;
|
||||||
fprint(netfd,
|
if(fprint(webctlfd, "request HEAD\n") < 0)
|
||||||
"HEAD %s HTTP/1.1\r\n"
|
return -1;
|
||||||
"Host: %s\r\n"
|
snprint(buf, sizeof(buf), "%s/body", webconn);
|
||||||
"Accept-Encoding:\r\n"
|
if((fd = open(buf, OREAD)) < 0)
|
||||||
"\r\n",
|
return -1;
|
||||||
get, host);
|
snprint(buf, sizeof(buf), "%s/contentlength", webconn);
|
||||||
|
cfd = open(buf, OREAD);
|
||||||
status = readhttphdr(&netbio, &size);
|
close(fd);
|
||||||
if(strstr(status, "200 OK") == nil){
|
if(cfd < 0)
|
||||||
werrstr("%s", status);
|
return -1;
|
||||||
size = -1;
|
if(readstring(cfd, buf, sizeof(buf)) <= 0){
|
||||||
|
close(cfd);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
free(status);
|
close(cfd);
|
||||||
|
return strtoll(buf, nil, 10);
|
||||||
close(netfd);
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -443,7 +397,8 @@ fileread(Req *r)
|
||||||
{
|
{
|
||||||
Block *b;
|
Block *b;
|
||||||
|
|
||||||
if(r->ifcall.offset > size){
|
if(r->ifcall.offset >= size){
|
||||||
|
r->ofcall.count = 0;
|
||||||
respond(r, nil);
|
respond(r, nil);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -476,7 +431,7 @@ void
|
||||||
finishthread(void*)
|
finishthread(void*)
|
||||||
{
|
{
|
||||||
Block *b;
|
Block *b;
|
||||||
Req *r, *nextr;
|
Req *r;
|
||||||
|
|
||||||
threadsetname("finishthread");
|
threadsetname("finishthread");
|
||||||
|
|
||||||
|
@ -488,11 +443,11 @@ finishthread(void*)
|
||||||
if(ncache >= mcache)
|
if(ncache >= mcache)
|
||||||
evictblock(&cache);
|
evictblock(&cache);
|
||||||
addblock(&cache, b);
|
addblock(&cache, b);
|
||||||
for(r=b->rq; r; r=nextr){
|
while((r = b->rq) != nil){
|
||||||
nextr = r->aux;
|
b->rq = r->aux;
|
||||||
|
r->aux = nil;
|
||||||
readfrom(r, b);
|
readfrom(r, b);
|
||||||
}
|
}
|
||||||
b->rq = nil;
|
|
||||||
if(inprogress.first)
|
if(inprogress.first)
|
||||||
sendp(httpchan, inprogress.first);
|
sendp(httpchan, inprogress.first);
|
||||||
}
|
}
|
||||||
|
@ -501,7 +456,7 @@ finishthread(void*)
|
||||||
void
|
void
|
||||||
fsnetproc(void*)
|
fsnetproc(void*)
|
||||||
{
|
{
|
||||||
Req *r;
|
Req *r, *o;
|
||||||
Block *b;
|
Block *b;
|
||||||
|
|
||||||
threadcreate(finishthread, nil, 8192);
|
threadcreate(finishthread, nil, 8192);
|
||||||
|
@ -512,9 +467,11 @@ fsnetproc(void*)
|
||||||
r = recvp(reqchan);
|
r = recvp(reqchan);
|
||||||
switch(r->ifcall.type){
|
switch(r->ifcall.type){
|
||||||
case Tflush:
|
case Tflush:
|
||||||
b = findblock(&inprogress, r->ifcall.offset);
|
o = r->oldreq;
|
||||||
delreq(b, r->oldreq);
|
b = findblock(&inprogress, o->ifcall.offset);
|
||||||
respond(r->oldreq, "interrupted");
|
if(b != nil)
|
||||||
|
delreq(b, o);
|
||||||
|
respond(o, "interrupted");
|
||||||
respond(r, nil);
|
respond(r, nil);
|
||||||
break;
|
break;
|
||||||
case Tread:
|
case Tread:
|
||||||
|
@ -569,7 +526,7 @@ Srv fs =
|
||||||
void
|
void
|
||||||
threadmain(int argc, char **argv)
|
threadmain(int argc, char **argv)
|
||||||
{
|
{
|
||||||
char *defport, *mtpt, *srvname, *p;
|
char *mtpt, *srvname, *p;
|
||||||
|
|
||||||
mtpt = nil;
|
mtpt = nil;
|
||||||
srvname = nil;
|
srvname = nil;
|
||||||
|
@ -592,9 +549,6 @@ threadmain(int argc, char **argv)
|
||||||
case 'f':
|
case 'f':
|
||||||
file = EARGF(usage());
|
file = EARGF(usage());
|
||||||
break;
|
break;
|
||||||
case 'x':
|
|
||||||
net = smprint("%s/net", EARGF(usage()));
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
}ARGEND;
|
}ARGEND;
|
||||||
|
@ -608,37 +562,22 @@ threadmain(int argc, char **argv)
|
||||||
mcache = 32;
|
mcache = 32;
|
||||||
|
|
||||||
time0 = time(0);
|
time0 = time(0);
|
||||||
host = url = estrdup9p(argv[0]);
|
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){
|
if(file == nil){
|
||||||
file = strrchr(get, '/')+1;
|
file = strrchr(url, '/');
|
||||||
if(*file == 0)
|
if(file == nil || file[1] == '\0')
|
||||||
file = "index";
|
file = "index";
|
||||||
|
else
|
||||||
|
file++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
snprint(webconn, sizeof(webconn), "/mnt/web/clone");
|
||||||
|
if((webctlfd = open(webconn, ORDWR)) < 0)
|
||||||
|
sysfatal("open: %r");
|
||||||
|
p = strrchr(webconn, '/')+1;
|
||||||
|
if(readstring(webctlfd, p, webconn+sizeof(webconn)-p) <= 0)
|
||||||
|
sysfatal("read: %r");
|
||||||
|
|
||||||
tab[Qfile].name = file;
|
tab[Qfile].name = file;
|
||||||
user = getuser();
|
user = getuser();
|
||||||
size = getfilesize();
|
size = getfilesize();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue