git: plan9front

Download patch

ref: 36a8d961bf90a5b30e7fc9dedb53f07d485ef41f
parent: 69115adfe4050c7e0e88b26aea77d6dc829296d3
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Jun 1 09:39:37 EDT 2025

webfs: Provide errorbody file

Have a way to read the error response body
from the request.

--- a/sys/man/4/webfs
+++ b/sys/man/4/webfs
@@ -166,8 +166,19 @@
 .B parsed
 directory will change to the final destination.
 .PP
+Opening the
+.B body
+file can fail, returning HTTP status error in the
+error string.
+If more detailed error is required, the
+.B errorbody
+file can be opend, providing the servers error
+response (usually formatted as HTML).
+.PP
 The resulting data may be read from
 .B body
+and
+.B errorbody
 as it arrives.
 .PP
 The following is a list of attributes that can be
--- a/sys/src/cmd/webfs/buq.c
+++ b/sys/src/cmd/webfs/buq.c
@@ -156,6 +156,15 @@
 }
 
 void
+busethdrs(Buq *q, Url *u, Key *h)
+{
+	qlock(q);
+	q->hdr = h;
+	q->url = u;
+	qunlock(q);
+}
+
+void
 buclose(Buq *q, char *error)
 {
 	if(q == nil)
--- a/sys/src/cmd/webfs/fns.h
+++ b/sys/src/cmd/webfs/fns.h
@@ -28,6 +28,7 @@
 void	freeurl(Url *u);
 
 /* buq */
+void	busethdrs(Buq *q, Url *u, Key *h);
 int	buread(Buq *q, void *v, int l);
 int	buwrite(Buq *q, void *v, int l);
 void	buclose(Buq *q, char *error);
@@ -40,4 +41,4 @@
 /* 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, Buq *qerror);
--- a/sys/src/cmd/webfs/fs.c
+++ b/sys/src/cmd/webfs/fs.c
@@ -23,6 +23,7 @@
 	int	obody;	/* body opend */
 	int	cbody;	/* body closed */
 	Buq	*qbody;
+	Buq	*qerror;
 };
 
 struct Webfid
@@ -31,7 +32,7 @@
 
 	Client	*client;
 	Key	*key;	/* copy for Qheader */
-	Buq	*buq;	/* reference for Qbody, Qpost */
+	Buq	*buq;	/* reference for Qbody, Qpost, Qerror */
 };
 
 enum {
@@ -42,6 +43,7 @@
 			Qctl,
 			Qbody,
 			Qpost,
+			Qerror,
 			Qparsed,
 				Qurl,
 				Qurlschm,
@@ -63,6 +65,7 @@
 			"ctl",
 			"body",
 			"postbody",
+			"errorbody",
 			"parsed",
 				"url",
 				"scheme",
@@ -107,6 +110,7 @@
 	cl->url = nil;
 	cl->hdr = nil;
 	cl->qbody = nil;
+	cl->qerror = nil;
 	
 	return cl;
 }
@@ -119,8 +123,10 @@
 	if(cl == nil || decref(cl))
 		return;
 
-	buclose(cl->qbody, 0);
+	buclose(cl->qbody, nil);
+	buclose(cl->qerror, nil);
 	bufree(cl->qbody);
+	bufree(cl->qerror);
 
 	while(k = cl->hdr){
 		cl->hdr = k->next;
@@ -138,6 +144,8 @@
 {
 	static Url nullurl;
 
+	if(cl->qerror && cl->qerror->url)
+		return cl->qerror->url;
 	if(cl->qbody && cl->qbody->url)
 		return cl->qbody->url;
 	if(cl->url)
@@ -145,6 +153,16 @@
 	return &nullurl;
 }
 
+static Key*
+clienthdrs(Client *cl)
+{
+	if(cl->qerror && cl->qerror->hdr)
+		return cl->qerror->hdr;
+	if(cl->qbody && cl->qbody->hdr)
+		return cl->qbody->hdr;
+	return nil;
+}
+
 static void*
 wfaux(Webfid *f)
 {
@@ -307,13 +325,13 @@
 					break;
 				}
 			}
-			if(i == Qheader && f->client && f->client->qbody){
+			if(i == Qheader && f->client){
 				char buf[128];
 				Key *k;
 
-				for(k = f->client->qbody->hdr; k; k = k->next){
+				for(k = clienthdrs(f->client); k; k = k->next){
 					nstrcpy(buf, k->key, sizeof(buf));
-					if(!strcmp(name, fshdrname(buf)))
+					if(strcmp(name, fshdrname(buf)) == 0)
 						break;
 				}
 				if(k != nil){
@@ -377,8 +395,13 @@
 		if(cl->obody)
 			goto Inuse;
 		if(cl->cbody){
+			buclose(cl->qerror, nil);
+			bufree(cl->qerror);
+			cl->qerror = nil;
+			buclose(cl->qbody, nil);
 			bufree(cl->qbody);
 			cl->qbody = nil;
+
 			cl->cbody = 0;
 		}
 		if(cl->qbody == nil){
@@ -389,7 +412,7 @@
 				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", 
@@ -413,7 +436,8 @@
 			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);
+			cl->qerror = bualloc(16*1024);
+			http(m, cl->url, cl->hdr, cl->qbody, f->buq, cl->qerror);
 			cl->request[0] = 0;
 			cl->url = nil;
 			cl->hdr = nil;
@@ -424,6 +448,16 @@
 		incref(cl->qbody);
 		bureq(f->buq = cl->qbody, r);
 		return;
+	case Qerror:
+		if(cl->qerror == nil){
+			respond(r, "request not started");
+			return;
+		}
+		if(f->buq)
+			break;
+		incref(cl->qerror);
+		f->buq = cl->qerror;
+		break;	
 	}
 	respond(r, nil);
 }
@@ -453,9 +487,9 @@
 		Key *k;
 
 		i -= Qparsed+1;
-		if(cl == nil || cl->qbody == nil)
+		if(cl == nil)
 			return -1;
-		for(k = cl->qbody->hdr; i > 0 && k; i--, k = k->next)
+		for(k = clienthdrs(cl); i > 0 && k; i--, k = k->next)
 			;
 		if(k == nil || i > 0)
 			return -1;
@@ -520,6 +554,7 @@
 		urlstr(buf, sizeof(buf), clienturl(f->client), f->level);
 		goto String;
 	case Qbody:
+	case Qerror:
 		bureq(f->buq, r);
 		return;
 	}
@@ -707,7 +742,7 @@
 	if(f = fid->aux){
 		fid->aux = nil;
 		if(f->buq){
-			buclose(f->buq, 0);
+			buclose(f->buq, nil);
 			if(f->client->qbody == f->buq){
 				f->client->obody = 0;
 				f->client->cbody = 1;
@@ -714,8 +749,7 @@
 			}
 			bufree(f->buq);
 		}
-		if(f->key)
-			free(f->key);
+		if(f->key) free(f->key);
 		freeclient(f->client);
 		free(f);
 	}
--- a/sys/src/cmd/webfs/http.c
+++ b/sys/src/cmd/webfs/http.c
@@ -546,7 +546,7 @@
 #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, Buq *qerror)
 {
 	int i, l, n, try, pid, fd, cfd, needlength, chunked, retry, nobody, badauth;
 	char *s, *x, buf[IOUNIT+2], status[256], method[16], *host;
@@ -558,6 +558,7 @@
 
 	incref(qbody);
 	if(qpost) incref(qpost);
+	incref(qerror);
 	nstrcpy(method, m, sizeof(method));
 	switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
 	default:
@@ -567,6 +568,8 @@
 		bufree(qbody);
 		buclose(qpost, "can't fork");
 		bufree(qpost);
+		buclose(qerror, "can't fork");
+		bufree(qerror);
 		while(k = shdr){
 			shdr = k->next;
 			free(k);
@@ -875,6 +878,11 @@
 		case 504:	/* Gateway Timeout */
 		case 505:	/* HTTP Version not Supported */
 		Error:
+			if(!retry){
+				busethdrs(qerror, u, rhdr);
+				rhdr = nil;
+				u = nil; 
+			}
 			h->cancel = 1;
 			buclose(qbody, status);
 			buclose(qpost, status);
@@ -887,7 +895,7 @@
 					waitpid();
 					pid = 0;
 				}
-				buclose(qpost, 0);
+				buclose(qpost, nil);
 				bufree(qpost);
 				qpost = nil;
 			}
@@ -956,10 +964,12 @@
 		case 206:	/* Partial Content */
 			if(h->tunnel)
 				break;
-			qbody->url = u; u = nil;
-			qbody->hdr = rhdr; rhdr = nil;
+			busethdrs(qbody, u, rhdr);
+			rhdr = nil;
+			u = nil;
+			buclose(qerror, nil);	/* no error */
 			if(nobody)
-				buclose(qbody, 0);
+				buclose(qbody, nil);
 			break;
 		}
 
@@ -998,9 +1008,9 @@
 		 * skipping the error page so we wont touch qbody.
 		 */
 		while(!nobody){
-			if((qbody->closed || retry) && !h->keep)
+			if((qerror->closed && qbody->closed || retry) && !h->keep)
 				break;
-			if(chunked){
+ 			if(chunked){
 				if(hline(h, buf, sizeof(buf)-1, 0) <= 0)
 					break;
 				length = strtoll(buf, nil, 16);
@@ -1014,7 +1024,7 @@
 					break;
 				offset += n;
 				if(!retry)
-					if(buwrite(qbody, buf, n) != n)
+					if(buwrite(qerror->closed?qbody:qerror, buf, n) != n)
 						break;
 			}
 			if(offset != length){
@@ -1035,8 +1045,10 @@
 				if(length > 0)
 					continue;
 			}
-			if(!retry)
-				buclose(qbody, 0);
+			if(!retry){
+				buclose(qerror, nil);
+				buclose(qbody, nil);
+			}
 			break;
 		}
 
@@ -1053,6 +1065,9 @@
 		h = nil;
 	}
 	alarm(0);
+	buclose(qerror, nil);
+	bufree(qerror);
+
 	snprint(buf, sizeof(buf), "%s %r", status);
 	buclose(qbody, buf);
 	bufree(qbody);
--