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