ref: 77f14aa86695e59554eb4698436ce1b76b93bdd2
parent: 22383ae54cfeb178b150dc9417b0d773cc72c434
author: Ori Bernstein <[email protected]>
date: Mon Feb 10 00:10:49 EST 2020
add http support
--- a/fetch.c
+++ b/fetch.c
@@ -136,12 +136,12 @@
}
int
-fetchpack(int fd, int pfd, char *packtmp)
+fetchpack(Conn *c, int pfd, char *packtmp)
{
char buf[Pktmax], idxtmp[256], *sp[3];
Hash h, *have, *want;
int nref, refsz;
- int i, n, req;
+ int i, j, n, req;
vlong packsz;
Object *o;
@@ -150,7 +150,7 @@
have = emalloc(refsz * sizeof(have[0]));
want = emalloc(refsz * sizeof(want[0]));
while(1){
- n = readpkt(fd, buf, sizeof(buf));
+ n = readpkt(c, buf, sizeof(buf));
if(n == -1)
return -1;
if(n == 0)
@@ -175,6 +175,8 @@
nref++;
}
+ if(writephase(c) == -1)
+ sysfatal("write: %r");
req = 0;
for(i = 0; i < nref; i++){
if(hasheq(&have[i], &want[i]))
@@ -183,42 +185,44 @@
unref(o);
continue;
}
- n = snprint(buf, sizeof(buf), "want %H", want[i]);
- if(writepkt(fd, buf, n) == -1)
+ n = snprint(buf, sizeof(buf), "want %H\n", want[i]);
+ if(writepkt(c, buf, n) == -1)
sysfatal("could not send want for %H", want[i]);
- req = 1;
+ req = 1;
}
- flushpkt(fd);
+ flushpkt(c);
for(i = 0; i < nref; i++){
if(hasheq(&have[i], &Zhash))
continue;
n = snprint(buf, sizeof(buf), "have %H\n", have[i]);
- if(writepkt(fd, buf, n + 1) == -1)
+ if(writepkt(c, buf, n + 1) == -1)
sysfatal("could not send have for %H", have[i]);
}
- if(!req){
- flushpkt(fd);
- }
+ if(!req)
+ flushpkt(c);
+
n = snprint(buf, sizeof(buf), "done\n");
- if(writepkt(fd, buf, n) == -1)
- sysfatal("lost connection write");
+ if(writepkt(c, buf, n) == -1)
+ sysfatal("write: %r");
if(!req)
return 0;
-
- if((n = readpkt(fd, buf, sizeof(buf))) == -1)
- sysfatal("lost connection read");
+ if(readphase(c) == -1)
+ sysfatal("read: %r");
+ if((n = readpkt(c, buf, sizeof(buf))) == -1)
+ sysfatal("read: %r");
buf[n] = 0;
fprint(2, "fetching...\n");
packsz = 0;
while(1){
- n = readn(fd, buf, sizeof buf);
+ n = readn(c->rfd, buf, sizeof buf);
if(n == 0)
break;
if(n == -1 || write(pfd, buf, n) != n)
- sysfatal("could not fetch packfile: %r");
+ sysfatal("fetch packfile: %r");
packsz += n;
}
+ closeconn(c);
if(seek(pfd, 0, 0) == -1)
sysfatal("packfile seek: %r");
if(checkhash(pfd, packsz, &h) == -1)
@@ -248,7 +252,8 @@
{
char proto[Nproto], host[Nhost], port[Nport];
char repo[Nrepo], path[Npath];
- int fd, pfd;
+ int r, pfd;
+ Conn c;
ARGBEGIN{
case 'b': fetchbranch=EARGF(usage()); break;
@@ -260,8 +265,8 @@
gitinit();
if(argc != 1)
usage();
- fd = -1;
+ r = -1;
if(mkoutpath(packtmp) == -1)
sysfatal("could not create %s: %r", packtmp);
if((pfd = create(packtmp, ORDWR, 0644)) == -1)
@@ -270,17 +275,18 @@
if(parseuri(argv[0], proto, host, port, path, repo) == -1)
sysfatal("bad uri %s", argv[0]);
if(strcmp(proto, "ssh") == 0 || strcmp(proto, "git+ssh") == 0)
- fd = dialssh(host, port, path, "upload");
+ r = dialssh(&c, host, port, path, "upload");
else if(strcmp(proto, "git") == 0)
- fd = dialgit(host, port, path, "upload");
- else if(strcmp(proto, "http") == 0 || strcmp(proto, "git+http") == 0)
- sysfatal("http clone not implemented");
+ r = dialgit(&c, host, port, path, "upload");
+ else if(strcmp(proto, "http") == 0 || strcmp(proto, "https") == 0)
+ r = dialhttp(&c, host, port, path, "upload");
else
sysfatal("unknown protocol %s", proto);
- if(fd == -1)
+ if(r == -1)
sysfatal("could not dial %s:%s: %r", proto, host);
- if(fetchpack(fd, pfd, packtmp) == -1)
+ if(fetchpack(&c, pfd, packtmp) == -1)
sysfatal("fetch failed: %r");
+ closeconn(&c);
exits(nil);
}
--- a/git.h
+++ b/git.h
@@ -4,6 +4,7 @@
#include <flate.h>
#include <regexp.h>
+typedef struct Conn Conn;
typedef struct Hash Hash;
typedef struct Cinfo Cinfo;
typedef struct Tinfo Tinfo;
@@ -30,7 +31,7 @@
Nbranch = 32,
};
-typedef enum {
+enum {
GNone = 0,
GCommit = 1,
GTree = 2,
@@ -48,6 +49,12 @@
Cparsed = 1 << 5,
};
+enum {
+ ConnRaw,
+ ConnSsh,
+ ConnHttp,
+};
+
struct Objlist {
int idx;
@@ -72,6 +79,18 @@
uchar h[20];
};
+struct Conn {
+ int type;
+ int rfd;
+ int wfd;
+
+ /* only used by http */
+ int cfd;
+ char *url; /* note, first GET uses a different url */
+ char *dir;
+ char *direction;
+};
+
struct Dirent {
char *name;
int mode;
@@ -234,9 +253,13 @@
void die(char *, ...);
/* proto handling */
-int readpkt(int, char*, int);
-int writepkt(int, char*, int);
-int flushpkt(int);
+int readpkt(Conn*, char*, int);
+int writepkt(Conn*, char*, int);
+int flushpkt(Conn*);
int parseuri(char *, char *, char *, char *, char *, char *);
-int dialssh(char *, char *, char *, char *);
-int dialgit(char *, char *, char *, char *);
+int dialssh(Conn*, char *, char *, char *, char *);
+int dialgit(Conn*, char *, char *, char *, char *);
+int dialhttp(Conn*, char *, char *, char *, char *);
+int readphase(Conn *);
+int writephase(Conn *);
+void closeconn(Conn *);
--- a/proto.c
+++ b/proto.c
@@ -3,16 +3,20 @@
#include "git.h"
+#define Useragent "useragent git/2.24.1"
+#define Contenthdr "headers Content-Type: application/x-git-%s-pack-request"
+#define Accepthdr "headers Accept: application/x-git-%s-pack-result"
+
int chattygit;
int
-readpkt(int fd, char *buf, int nbuf)
+readpkt(Conn *c, char *buf, int nbuf)
{
char len[5];
char *e;
int n;
- if(readn(fd, len, 4) == -1)
+ if(readn(c->rfd, len, 4) == -1)
return -1;
len[4] = 0;
n = strtol(len, &e, 16);
@@ -26,7 +30,7 @@
n -= 4;
if(n >= nbuf)
sysfatal("buffer too small");
- if(readn(fd, buf, n) != n)
+ if(readn(c->rfd, buf, n) != n)
return -1;
buf[n] = 0;
if(chattygit)
@@ -35,15 +39,15 @@
}
int
-writepkt(int fd, char *buf, int nbuf)
+writepkt(Conn *c, char *buf, int nbuf)
{
char len[5];
snprint(len, sizeof(len), "%04x", nbuf + 4);
- if(write(fd, len, 4) != 4)
+ if(write(c->wfd, len, 4) != 4)
return -1;
- if(write(fd, buf, nbuf) != nbuf)
+ if(write(c->wfd, buf, nbuf) != nbuf)
return -1;
if(chattygit){
fprint(2, "writepkt: %s:\t", len);
@@ -54,11 +58,11 @@
}
int
-flushpkt(int fd)
+flushpkt(Conn *c)
{
if(chattygit)
fprint(2, "writepkt: 0000\n");
- return write(fd, "0000", 4);
+ return write(c->wfd, "0000", 4);
}
static void
@@ -85,8 +89,20 @@
werrstr("missing protocol");
return -1;
}
- grab(proto, Nproto, uri, p);
- hasport = (strcmp(proto, "git") == 0 || strstr(proto, "http") == proto);
+ if(strncmp(uri, "git+", 4) == 0)
+ grab(proto, Nproto, uri + 4, p);
+ else
+ grab(proto, Nproto, uri, p);
+ *port = 0;
+ hasport = 1;
+ if(strcmp(proto, "git") == 0)
+ snprint(port, Nport, "9418");
+ else if(strncmp(proto, "https", 5) == 0)
+ snprint(port, Nport, "443");
+ else if(strncmp(proto, "http", 4) == 0)
+ snprint(port, Nport, "80");
+ else
+ hasport = 0;
s = p + 3;
p = nil;
if(!hasport){
@@ -107,7 +123,6 @@
grab(port, Nport, q + 1, p);
}else{
grab(host, Nhost, s, p);
- snprint(port, Nport, "9418");
}
snprint(path, Npath, "%s", p);
@@ -124,8 +139,100 @@
}
int
-dialssh(char *host, char *, char *path, char *direction)
+webclone(Conn *c, char *url)
{
+ char buf[16];
+ int n, conn;
+
+ if((c->cfd = open("/mnt/web/clone", ORDWR)) < 0)
+ goto err;
+ if((n = read(c->cfd, buf, sizeof(buf)-1)) == -1)
+ goto err;
+ buf[n] = 0;
+ conn = atoi(buf);
+
+ /* github will behave differently based on useragent */
+ if(write(c->cfd, Useragent, sizeof(Useragent)) == -1)
+ return -1;
+ if(chattygit)
+ fprint(2, "open url %s\n", url);
+ if(fprint(c->cfd, "url %s", url) == -1)
+ goto err;
+ free(c->dir);
+ c->dir = smprint("/mnt/web/%d", conn);
+ return 0;
+err:
+ if(c->cfd != -1)
+ close(c->cfd);
+ return -1;
+}
+
+int
+webopen(Conn *c, char *file, int mode)
+{
+ char path[128];
+ int fd;
+
+ snprint(path, sizeof(path), "%s/%s", c->dir, file);
+ if((fd = open(path, mode)) == -1)
+ return -1;
+ return fd;
+}
+
+int
+issmarthttp(Conn *c, char *direction)
+{
+ char buf[Pktmax+1], svc[128];
+ int n;
+
+ if((n = readpkt(c, buf, sizeof(buf))) == -1)
+ sysfatal("http read: %r");
+ buf[n] = 0;
+ snprint(svc, sizeof(svc), "# service=git-%s-pack\n", direction);
+ if(strncmp(svc, buf, n) != 0){
+ werrstr("dumb http protocol not supported");
+ return -1;
+ }
+ if(readpkt(c, buf, sizeof(buf)) != 0){
+ werrstr("protocol garble: expected flushpkt");
+ return -1;
+ }
+ return 0;
+}
+
+int
+dialhttp(Conn *c, char *host, char *port, char *path, char *direction)
+{
+ char *geturl, *suff, *hsep, *psep;
+
+ suff = "";
+ hsep = "";
+ psep = "";
+ if(port && strlen(port) != 0)
+ hsep = ":";
+ if(path && path[0] != '/')
+ psep = "/";
+ memset(c, 0, sizeof(*c));
+ geturl = smprint("https://%s%s%s%s%s%s/info/refs?service=git-%s-pack", host, hsep, port, psep, path, suff, direction);
+ c->type = ConnHttp;
+ c->url = smprint("https://%s%s%s%s%s%s/git-%s-pack", host, hsep, port, psep, path, suff, direction);
+ c->cfd = webclone(c, geturl);
+ free(geturl);
+ if(c->cfd == -1)
+ return -1;
+ c->rfd = webopen(c, "body", OREAD);
+ c->wfd = -1;
+ if(c->rfd == -1)
+ return -1;
+ if(issmarthttp(c, direction) == -1)
+ return -1;
+ c->direction = estrdup(direction);
+ return 0;
+}
+
+int
+dialssh(Conn *c, char *host, char *, char *path, char *direction)
+{
int pid, pfd[2];
char cmd[64];
@@ -144,31 +251,99 @@
execl("/bin/ssh", "ssh", host, cmd, path, nil);
}else{
close(pfd[0]);
- return pfd[1];
+ c->type = ConnSsh;
+ c->rfd = pfd[1];
+ c->wfd = dup(pfd[1], -1);
+ return 0;
}
return -1;
}
int
-dialgit(char *host, char *port, char *path, char *direction)
+dialgit(Conn *c, char *host, char *port, char *path, char *direction)
{
char *ds, *p, *e, cmd[512];
int fd;
ds = netmkaddr(host, "tcp", port);
+ if(chattygit)
+ fprint(2, "dial %s git-%s-pack %s\n", ds, direction, path);
fd = dial(ds, nil, nil, nil);
if(fd == -1)
return -1;
- if(chattygit)
- fprint(2, "dial %s %s git-%s-pack %s\n", host, port, direction, path);
p = cmd;
e = cmd + sizeof(cmd);
p = seprint(p, e - 1, "git-%s-pack %s", direction, path);
p = seprint(p + 1, e, "host=%s", host);
- if(writepkt(fd, cmd, p - cmd + 1) == -1){
- print("failed to write message\n");
+ c->type = ConnRaw;
+ c->rfd = fd;
+ c->wfd = dup(fd, -1);
+ if(writepkt(c, cmd, p - cmd + 1) == -1){
+ fprint(2, "failed to write message\n");
close(fd);
return -1;
}
- return fd;
+ return 0;
+}
+
+int
+writephase(Conn *c)
+{
+ char hdr[128];
+ int n;
+
+ if(chattygit)
+ fprint(2, "start write phase\n");
+ if(c->type != ConnHttp)
+ return 0;
+
+ if(c->wfd != -1)
+ close(c->wfd);
+ if(c->cfd != -1)
+ close(c->cfd);
+ if((c->cfd = webclone(c, c->url)) == -1)
+ return -1;
+ n = snprint(hdr, sizeof(hdr), Contenthdr, c->direction);
+ if(write(c->cfd, hdr, n) == -1)
+ return -1;
+ n = snprint(hdr, sizeof(hdr), Accepthdr, c->direction);
+ if(write(c->cfd, hdr, n) == -1)
+ return -1;
+ if((c->wfd = webopen(c, "postbody", OWRITE)) == -1)
+ return -1;
+ c->rfd = -1;
+ return 0;
+}
+
+int
+readphase(Conn *c)
+{
+ if(chattygit)
+ fprint(2, "start read phase\n");
+ if(c->type != ConnHttp)
+ return 0;
+ if(close(c->wfd) == -1)
+ return -1;
+ if((c->rfd = webopen(c, "body", OREAD)) == -1)
+ return -1;
+ c->wfd = -1;
+ return 0;
+}
+
+void
+closeconn(Conn *c)
+{
+ close(c->rfd);
+ close(c->wfd);
+ switch(c->type){
+ case ConnRaw:
+
+ break;
+ case ConnSsh:
+ free(wait());
+ break;
+ case ConnHttp:
+ close(c->cfd);
+ break;
+ }
}
--- a/send.c
+++ b/send.c
@@ -152,7 +152,7 @@
}
int
-writepack(int fd, Update *upd, int nupd)
+writepack(Conn *c, Update *upd, int nupd)
{
Objset send, skip;
Object *o, *p;
@@ -216,18 +216,18 @@
st = nil;
PUTBE32(buf, send.nobj);
- if(hwrite(fd, "PACK\0\0\0\02", 8, &st) != 8)
+ if(hwrite(c->wfd, "PACK\0\0\0\02", 8, &st) != 8)
return -1;
- if(hwrite(fd, buf, 4, &st) == -1)
+ if(hwrite(c->wfd, buf, 4, &st) == -1)
return -1;
for(i = 0; i < send.sz; i++){
if(!send.obj[i])
continue;
- if(writeobject(fd, send.obj[i], &st) == -1)
+ if(writeobject(c->wfd, send.obj[i], &st) == -1)
return -1;
}
sha1(nil, 0, h.h, st);
- if(write(fd, h.h, sizeof(h.h)) == -1)
+ if(write(c->wfd, h.h, sizeof(h.h)) == -1)
return -1;
return 0;
}
@@ -279,7 +279,7 @@
}
int
-sendpack(int fd)
+sendpack(Conn *c)
{
int i, n, r, nupd, nsp, send;
char buf[Pktmax], *sp[3];
@@ -288,7 +288,7 @@
nupd = readours(&upd);
while(1){
- n = readpkt(fd, buf, sizeof(buf));
+ n = readpkt(c, buf, sizeof(buf));
if(n == -1)
return -1;
if(n == 0)
@@ -305,6 +305,8 @@
snprint(u->ref, sizeof(u->ref), sp[1]);
}
+ if(writephase(c) == -1)
+ return -1;
r = 0;
send = 0;
for(i = 0; i < nupd; i++){
@@ -343,7 +345,7 @@
buf[n++] = '\0';
n += snprint(buf + n, sizeof(buf) - n, " report-status");
}
- if(writepkt(fd, buf, n) == -1)
+ if(writepkt(c, buf, n) == -1)
sysfatal("unable to send update pkt");
/*
* If we're rolling back with a force push, the other side already
@@ -352,17 +354,19 @@
if(a == nil || b == nil || ancestor(b, a) != b)
send = 1;
}
- flushpkt(fd);
+ flushpkt(c);
if(!send)
print("nothing to send\n");
if(send){
if(chattygit)
fprint(2, "sending pack...\n");
- if(writepack(fd, upd, nupd) == -1)
+ if(writepack(c, upd, nupd) == -1)
return -1;
+ if(readphase(c) == -1)
+ return -1;
/* We asked for a status report, may as well use it. */
- while((n = readpkt(fd, buf, sizeof(buf))) > 0){
+ while((n = readpkt(c, buf, sizeof(buf))) > 0){
buf[n] = 0;
if(chattygit)
fprint(2, "done sending pack, status %s\n", buf);
@@ -400,20 +404,27 @@
char proto[Nproto], host[Nhost], port[Nport];
char repo[Nrepo], path[Npath];
char *br;
- int fd;
+ Conn c;
+ int r;
ARGBEGIN{
default:
- usage(); break;
+ usage();
+ break;
case 'd':
- chattygit++; break;
+ chattygit++;
+ break;
case 'f':
- force++; break;
+ force++;
+ break;
case 'r':
if(nremoved == nelem(removed))
sysfatal("too many deleted branches");
removed[nremoved++] = EARGF(usage());
break;
+ case 'a':
+ sendall++;
+ break;
case 'b':
br = EARGF(usage());
if(strncmp(br, "refs/heads/", strlen("refs/heads/")) == 0)
@@ -431,21 +442,23 @@
gitinit();
if(argc != 1)
usage();
- fd = -1;
+ r = -1;
if(parseuri(argv[0], proto, host, port, path, repo) == -1)
sysfatal("bad uri %s", argv[0]);
- if(strcmp(proto, "ssh") == 0 || strcmp(proto, "git+ssh") == 0)
- fd = dialssh(host, port, path, "receive");
+
+ if(strcmp(proto, "ssh") == 0)
+ r = dialssh(&c, host, port, path, "receive");
else if(strcmp(proto, "git") == 0)
- fd = dialgit(host, port, path, "receive");
- else if(strcmp(proto, "http") == 0 || strcmp(proto, "git+http") == 0)
- sysfatal("http clone not implemented");
+ r = dialgit(&c, host, port, path, "receive");
+ else if(strcmp(proto, "http") == 0 || strcmp(proto, "https") == 0)
+ r = dialhttp(&c, host, port, path, "receive");
else
sysfatal("unknown protocol %s", proto);
- if(fd == -1)
+ if(r == -1)
sysfatal("could not dial %s:%s: %r", proto, host);
- if(sendpack(fd) == -1)
+ if(sendpack(&c) == -1)
sysfatal("send failed: %r");
+ closeconn(&c);
exits(nil);
}