Plan 9 from Bell Labs’s /usr/web/sources/contrib/maht/limbo/fastcgi.b

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


implement FastCGI;

include "sys.m";
	sys: Sys;

include "draw.m";
	draw: Draw;

include "bufio.m";
	bufio: Bufio;
	Iobuf: import bufio;

include "fastcgi.m";

log : chan of string;

logger()
{
	logf := bufio->open("/usr/maht/fastcgi/fastcgi.log", sys->OWRITE);
	while((txt := <- log) != nil) {
		logf.puts(txt + "\n");
		logf.flush();
	}
}

Record.tostring(r : self ref Record) : string
{
	pick rt := r {

	Begin_Request =>
		role : string;
		case rt.role {
		1 =>
			role = "Responder";
		2 =>
			role = "Authorizer";
		3 =>
			role = "Filter";
		* => 
			role = "UNKNOWN!";
		}
		return sys->sprint("Begin_Request{Role:%s, Flags:%d, Res:%s}", role, int rt.flags, string rt.reserved);

	Abort_Request =>
		return "Abort_Request";

	End_Request =>
		pstatus : string;
		case int rt.protocol_status {
		0 =>
			pstatus = "Request complete";
		1 =>
			pstatus = "Can't multiplex connection";
		2 =>
			pstatus = "Overloaded";
		3 =>
			pstatus = "Unknown Role";
		* =>
			pstatus = "UNKNOWN!";
		}
		return sys->sprint("End_Request{appStatus: %d, protoStatus: %s, Res: %s}", rt.application_status, pstatus, string rt.reserved);

	Params =>
		rstring := "Params{";
		if(rt.params != nil) {
			(k,v) := hd rt.params;
			rstring += k + ": " + v;
			for(t := tl rt.params; t != nil; t = tl t) {
				(k,v) = hd t;
				rstring += ", " + k + ": " + v;
			}
		}
		return rstring += "}";

	Stdin =>
		if(rt.data == nil)
			return "Stdin{}";
		return "Stdin{" + string rt.data + "}";

	Stdout =>
		if(rt.data == nil)
			return "Stdout{}";
		return "Stdout{" + string rt.data + "}";

	Stderr =>
		if(rt.data == nil)
			return "Stderr{}";
		return "Stderr{" + string rt.data+ "}";
	Data =>
		if(rt.data == nil)
			return "Data{}";
		return "Data{" + string rt.data + "}";

	Get_Values =>
		rstring := "Get_Values{";
		if(rt.values != nil) {
			(k,v) := hd rt.values;
			rstring += k + ": " + v;
			for(t := tl rt.values; t != nil; t = tl t) {
				(k,v) = hd t;
				rstring += ", " + k + ": " + v;
			}
		}
		return rstring += "}";

	Get_Values_Result =>
		rstring := "Get_Values_Result{";
		if(rt.results != nil) {
			(k,v) := hd rt.results;
			rstring += k + ": " + v;
			for(t := tl rt.results; t != nil; t = tl t) {
				(k,v) = hd t;
				rstring += ", " + k + ": " + v;
			}
		}
		return rstring += "}";

	* =>
		return "UNKNOWN!";
	}
}

Httpd.open_io(h : self ref Httpd) : int
{
	h.io = bufio->open(sys->sprint("%s/data", h.conn.dir), Bufio->ORDWR);

	if(h.io == nil) {
		log <- = sys->sprint("%s/data failed to open", h.conn.dir);
		return 0;
	}
	return 1;
}

Httpd.read_header(h : self ref Httpd) : (int, int, int, big)
{
	header := array[8] of byte;
	nread := h.io.read(header, 8);
	if(nread != 8) {
		log <- = sys->sprint("short record got %d bytes", nread); 
		return (-1,0,0,big 0);
	}

	if(header[0] != byte 1) {
		log <- = sys->sprint("Protocol version mismatch : expected 1 got %d", int header[0]);
		return (-1,0,0,big 0);
	}

	return ((int header[2] << 8) + int header[3], int header[1], (int header[4] << 8) + int header[5], big header[6]);
}

Httpd.read_varstring_length(h : self ref Httpd) : (int, int)
{
	b3 := h.io.getb();
	if(b3 < 0)
		return (1, b3);

	if((b3 & 16r80) == 0)
		return (1, b3);

	b2 := h.io.getb();
	if(b2 < 0)
		return (2, b2);
	b1 := h.io.getb();
	if(b1 < 0)
		return (3, b1);
	b0 := h.io.getb();
	if(b0 < 0)
		return (4, b0);

	return (4, ((b3 & 16r7f) << 24) + (b2 << 16) + (b1 << 8) + b0);
}

Httpd.read_name_value_pairs(h : self ref Httpd, content_length: int) : (int, list of (string, string))
{
	nvs : list of (string, string);
	nvs = nil;
	nread := 0;
	jread : int;
	name_length, value_length : int;
	name, value : array of byte;
	
	while(nread < content_length) {
		(jread, name_length) = h.read_varstring_length();
		nread += jread;
		if(name_length < 0) {
			log <- = sys->sprint("reading name_length : %r");
			return(nread, nil);
		}

		(jread, value_length) = h.read_varstring_length();
		nread += jread;
		if(value_length < 0) {
			log <- = sys->sprint("reading value_length : %r");
			return(nread, nil);
		}

		if((nread + name_length + value_length) > content_length) {
			log <- = sys->sprint("name/value too big for content_length : %r");
			return(nread, nil);
		}

		name = array[name_length] of byte;
		jread = h.io.read(name, name_length);
		if(jread < 0) {
			log <- = sys->sprint("reading name : %r");
			return(nread, nil);
		}

		nread += jread;
		if(jread != name_length)
			return(nread, nil);

		value = array[value_length] of byte;
		jread = h.io.read(value, value_length);
		if(jread < 0) {
			log <- = sys->sprint("reading value : %r");
			return(nread, nil);
		}

		nread += jread;
		if(jread != value_length)
			return(nread, nil);

		nvs = (string name, string value) :: nvs;
	}
	return (nread, nvs);
}

Httpd.read_records(h : self ref Httpd, consumer : chan of ref Record)
{
	if(!h.open_io()) return;

	r : ref Record;
	(id, rtype, content_length, padding) := h.read_header();
	nread : int;
	while(id >= 0) {
		r = nil;
		nread = 0;
		case rtype {
		BEGIN_REQUEST =>
			if(content_length == 8) {
				content := array[8] of byte;
				if((nread = h.io.read(content, 8)) == 8) {
					r = ref Record.Begin_Request((int content[0] << 8) + int content[1], content[2], content[3:8]);					
				}
			}
		ABORT_REQUEST =>
			if(content_length == 0)
				r = ref Record.Abort_Request();
		END_REQUEST =>
			if(content_length == 8) {
				content := array[8] of byte;
				if((nread = h.io.read(content, 8)) == 8) {
					r = ref Record.End_Request((int content[0] << 24) + (int content[1] << 16) + (int content[2] << 8) + int content[3], content[4], content[5:8]);					
				}
			}
		PARAMS =>
			if(content_length > 0) {
				(jread, name_values) := h.read_name_value_pairs(content_length);
				if(name_values != nil)
					r = ref Record.Params(name_values);
				nread = jread;
			} else {
				r = ref Record.Params(nil);
			}
		STDIN =>
			if(content_length > 0) {
				data := array[content_length] of byte;
				nread = h.io.read(data, content_length);
				if(nread == content_length)
					r = ref Record.Stdin(data);
			} else {
				r = ref Record.Stdin(nil);
			}
		STDOUT =>
			if(content_length > 0) {
				data := array[content_length] of byte;
				nread = h.io.read(data, content_length);
				if(nread == content_length)
					r = ref Record.Stdout(data);
			} else {
				r = ref Record.Stdout(nil);
			}
		STDERR =>
			if(content_length > 0) {
				data := array[content_length] of byte;
				nread = h.io.read(data, content_length);
				if(nread == content_length)
					r = ref Record.Stderr(data);
			} else {
				r = ref Record.Stderr(nil);
			}
		DATA =>
			if(content_length > 0) {
				data := array[content_length] of byte;
				nread = h.io.read(data, content_length);
				if(nread == content_length)
					r = ref Record.Data(data);
			} else {
				r = ref Record.Data(nil);
			}
		GET_VALUES =>
			if(content_length > 0) {
				(jread, name_values) := h.read_name_value_pairs(content_length);
				if(name_values != nil)
					r = ref Record.Get_Values(name_values);
				nread = jread;
			} else {
				r = ref Record.Get_Values(nil);
			}
		GET_VALUES_RESULT =>
			if(content_length > 0) {
				(jread, name_values) := h.read_name_value_pairs(content_length);
				if(name_values != nil)
					r = ref Record.Get_Values_Result(name_values);
				nread = jread;
			} else {
				r = ref Record.Get_Values_Result(nil);
			}
		* =>
			log <- = "Skipping Unknown!";
			h.io.seek(big content_length, Bufio->SEEKRELA);
		}

		if(r != nil)
			consumer <- = r;

		if(nread < 0) {
			log <- = sys->sprint("Read Error: %r");
			break;
		}

		if(nread > content_length) {
			log <- = "Read too much data";
			break;
		}

		if(nread < content_length) {
			log <- = sys->sprint("Record type %d discarded", rtype);
			h.io.seek(big (content_length - nread), Bufio->SEEKRELA);
		}

		if(padding > big 0)
			h.io.seek(padding, Bufio->SEEKRELA);

		(id, rtype, content_length, padding) = h.read_header();
	}
}


listener(addr : string, consumer : chan of ref Record)
{
	(i, socket) := sys->announce(addr);
	if(i != 0) {
		log <- = "listen failed";
		return;
	}
	err : int;
	server_id := 0;
	while(1) {
		h := ref Httpd;
		(err, h.conn) = sys->listen(socket);
		if(err == 0) {
			h.server_id = server_id++;
			spawn h.read_records(consumer);
		}
	}
}

log_record(r : ref Record) {
	log <- = r.tostring();
}

processor(records : chan of ref Record)
{
	for(r := <- records;;r = <- records)
		if(r != nil)
			spawn log_record(r);
}

init(nil: ref Draw->Context, argv: list of string)
{
	sys = load Sys Sys->PATH;
	bufio = load Bufio Bufio->PATH;
	draw = load Draw Draw->PATH;

	log = chan of string;
	records := chan of ref Record;
	spawn logger();

	for(h := hd argv; tl argv != nil; argv = tl argv) {
		log <- = h;
	}

	addr := "tcp!192.168.9.8!9888";

	log <- = "Starting on " + addr;

	spawn processor(records);
	listener(addr, records);
}
#		kill FastCGI;		rm *.dis

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.