diff 86e3e0791c239bbaaece242cf78fa56794b58a72 uncommitted --- a/sys/lib/dist/ndb/common +++ b/sys/lib/dist/ndb/common @@ -163,6 +163,7 @@ tcp=rje port=77 tcp=finger port=79 tcp=http port=80 +tcp=ws port=80 tcp=link port=87 tcp=supdup port=95 tcp=hostnames port=101 @@ -187,6 +188,7 @@ tcp=proxy port=402 tcp=proxyd port=404 tcp=https port=443 +tcp=wss port=443 tcp=cifs port=445 tcp=ssmtp port=465 tcp=rexec port=512 restricted= --- a/sys/src/cmd/webfs/dat.h +++ b/sys/src/cmd/webfs/dat.h @@ -2,6 +2,7 @@ typedef struct Buq Buq; typedef struct Buf Buf; typedef struct Key Key; +typedef struct Sok Sok; typedef struct { char *s1; @@ -62,6 +63,18 @@ Req **rt; Rendez rz; +}; + +struct Sok +{ + Ref; + QLock; + + int fd; + int ctl; + int closed; + + Rendez rz; }; int debug; --- a/sys/src/cmd/webfs/fns.h +++ b/sys/src/cmd/webfs/fns.h @@ -37,7 +37,14 @@ void bureq(Buq *q, Req *r); void buflushreq(Buq *q, Req *r); +/* sok */ +void soclose(Sok* s); +Sok* soalloc(void); +void sofree(Sok *s); + +void soreq(Sok *s, Req *r); + /* http */ int authenticate(Url *u, Url *ru, char *method, char *s); void flushauth(Url *u, char *t); -void http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost); +void http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost, Sok *socket); --- a/sys/src/cmd/webfs/fs.c +++ b/sys/src/cmd/webfs/fs.c @@ -32,6 +32,7 @@ Client *client; Key *key; /* copy for Qheader */ Buq *buq; /* reference for Qbody, Qpost */ + Sok *sok; }; enum { @@ -42,6 +43,7 @@ Qctl, Qbody, Qpost, + Qsocket, Qparsed, Qurl, Qurlschm, @@ -63,6 +65,7 @@ "ctl", "body", "postbody", + "socket", "parsed", "url", "scheme", @@ -373,6 +376,7 @@ respond(r, "client in use"); return; } + case Qsocket: case Qbody: if(cl->obody) goto Inuse; @@ -389,12 +393,18 @@ return; } cl->qbody = bualloc(16*1024); - if(f->level != Qbody){ + if(f->level == Qpost){ f->buq = bualloc(64*1024); if(!lookkey(cl->hdr, "Content-Type")) cl->hdr = addkey(cl->hdr, "Content-Type", "application/x-www-form-urlencoded"); m = "POST"; + } else if(f->level == Qsocket){ + f->sok = soalloc(); + /* TODO add the headers */ + //if(!lookkey(cl->hdr, "Upgrade")) + // cl->hdr = addkey(cl->hdr, "Upgrade", "websocket"); + m = "GET"; } else m = "GET"; if(cl->request[0]) @@ -413,7 +423,7 @@ if(agent && !lookkey(cl->hdr, "User-Agent")) cl->hdr = addkey(cl->hdr, "User-Agent", agent); - http(m, cl->url, cl->hdr, cl->qbody, f->buq); + http(m, cl->url, cl->hdr, cl->qbody, f->buq, f->sok); cl->request[0] = 0; cl->url = nil; cl->hdr = nil; @@ -520,6 +530,7 @@ urlstr(buf, sizeof(buf), clienturl(f->client), f->level); goto String; case Qbody: + case Qsocket: bureq(f->buq, r); return; } @@ -683,6 +694,9 @@ case Qpost: bureq(f->buq, r); return; + case Qsocket: + soreq(f->sok, r); + return; } respond(r, "not implemented"); } @@ -714,6 +728,10 @@ } bufree(f->buq); } + if(f->sok){ + soclose(f->sok); + sofree(f->sok); + } if(f->key) free(f->key); freeclient(f->client); @@ -809,7 +827,8 @@ if(s = getenv("httpproxy")){ proxy = saneurl(url(s, 0)); - if(proxy == nil || strcmp(proxy->scheme, "http") && strcmp(proxy->scheme, "https")) + if(proxy == nil || strcmp(proxy->scheme, "http") && strcmp(proxy->scheme, "https") + && strcmp(proxy->scheme, "ws") && strcmp(proxy->scheme, "wss")) sysfatal("invalid httpproxy url: %s", s); free(s); } --- a/sys/src/cmd/webfs/http.c +++ b/sys/src/cmd/webfs/http.c @@ -128,10 +128,10 @@ return nil; if(proxy){ - if(strcmp(proxy->scheme, "https") == 0) + if(strcmp(proxy->scheme, "https") == 0 || strcmp(proxy->scheme, "wss") == 0) fd = tlswrap(fd, proxy->host); } else { - if(strcmp(u->scheme, "https") == 0) + if(strcmp(u->scheme, "https") == 0 || strcmp(u->scheme, "wss") == 0) fd = tlswrap(fd, u->host); } if(fd < 0){ @@ -150,7 +150,7 @@ h->ctl = ctl; if(proxy){ - h->tunnel = strcmp(u->scheme, "https") == 0; + h->tunnel = strcmp(u->scheme, "https") == 0 || strcmp(u->scheme, "wss") == 0; snprint(addr, sizeof(addr), "tcp!%s!%s", u->host, u->port ? u->port : u->scheme); } nstrcpy(h->addr, addr, sizeof(h->addr)); @@ -546,9 +546,9 @@ #define NOLENGTH 0x7fffffffffffffffLL void -http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost) +http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost, Sok *socket) { - int i, l, n, try, pid, fd, cfd, needlength, chunked, retry, nobody, badauth; + int i, l, n, try, pid, fd, cfd, needlength, chunked, retry, nobody, badauth, issocket; char *s, *x, buf[IOUNIT+2], status[256], method[16], *host; vlong length, offset; Url ru, tu, *nu; @@ -591,9 +591,11 @@ host = nil; needlength = 0; badauth = 0; + issocket = 0; for(try = 0; try < 12; try++){ strcpy(status, "0 No status"); - if(u == nil || (strcmp(u->scheme, "http") && strcmp(u->scheme, "https"))){ + if(u == nil || (strcmp(u->scheme, "http") && strcmp(u->scheme, "https") + && strcmp(u->scheme, "ws") && strcmp(u->scheme, "wss"))){ werrstr("bad url scheme"); break; } @@ -658,7 +660,7 @@ free(host); host = smprint("%N", u->host); - if(proxy && strcmp(u->scheme, "https") != 0){ + if(proxy && strcmp(u->scheme, "https") != 0 && strcmp(u->scheme, "wss") != 0){ ru = *u; ru.host = host; ru.fragment = nil; @@ -832,10 +834,19 @@ goto Status; } goto Error; - case 100: /* Continue */ case 101: /* Switching Protocols */ + if((x = lookkey(rhdr, "Upgrade")) == nil) + goto Discard; + if(strcmp(x, "websocket") != 0) + goto Discard; + if(socket == nil) + nobody = 1; + issocket = 1; + goto Ok; + case 100: /* Continue */ case 102: /* Processing */ case 103: /* Early Hints */ + Discard: while(k = rhdr){ rhdr = k->next; free(k); @@ -954,6 +965,7 @@ case 202: /* Accepted */ case 203: /* Non-Authoritative Information */ case 206: /* Partial Content */ + Ok: if(h->tunnel) break; qbody->url = u; u = nil; @@ -992,6 +1004,15 @@ if(!chunked && length == NOLENGTH) h->keep = 0; + + if(issocket && socket != nil){ + /* TODO extract to a function in sok.c */ + qlock(socket); + socket->fd = h->fd; + socket->ctl = h->ctl; + rwakeup(&socket->rz); + qunlock(socket); + } /* * read the response body (if any). retry means we'r just --- a/sys/src/cmd/webfs/mkfile +++ b/sys/src/cmd/webfs/mkfile @@ -3,6 +3,6 @@ TARG=webfs HFILES=fns.h dat.h -OFILES=sub.$O url.$O buq.$O http.$O fs.$O +OFILES=sub.$O url.$O buq.$O http.$O fs.$O sok.$O +#include +#include +#include +#include +#include <9p.h> + +#include "dat.h" +#include "fns.h" + +void +soclose(Sok *s) +{ + if(s == nil) + return; + qlock(s); + s->closed = 1; + hangup(s->ctl); + close(s->fd); + close(s->ctl); + qunlock(s); +} + +Sok* +soalloc(void) +{ + Sok *s; + + s = emalloc(sizeof(*s)); + s->fd = -1; + s->closed = 0; + s->rz.l = s; + incref(s); + return s; +} + +void +sofree(Sok *s) +{ + if(s == nil || decref(s)) + return; + free(s); +} + +void +soreq(Sok *s, Req *r) +{ + long n; + + switch(r->ifcall.type){ + default: + respond(r, "bug in soreq"); + return; + case Twrite: + qlock(s); + while(s->fd < 0){ + rsleep(&s->rz); + } + if(s->closed){ + r->ofcall.count = r->ifcall.count; + qunlock(s); + respond(r, nil); + return; + } + if((n = write(s->fd, r->ifcall.data, r->ifcall.count)) < 0){ + qunlock(s); + responderror(r); + return; + } + r->ofcall.count = n; + qunlock(s); + respond(r, nil); + } +} --- a/sys/src/cmd/webfs/url.c +++ b/sys/src/cmd/webfs/url.c @@ -359,8 +359,8 @@ switch(atoi(u->port)){ case 21: if(!strcmp(u->scheme, "ftp")) goto Defport; break; case 70: if(!strcmp(u->scheme, "gopher"))goto Defport; break; - case 80: if(!strcmp(u->scheme, "http")) goto Defport; break; - case 443: if(!strcmp(u->scheme, "https")) goto Defport; break; + case 80: if(!strcmp(u->scheme, "http") || !strcmp(u->scheme, "ws")) goto Defport; break; + case 443: if(!strcmp(u->scheme, "https") || !strcmp(u->scheme, "wss")) goto Defport; break; default: if(!strcmp(u->scheme, u->port)) goto Defport; break; Defport: free(u->port);