Charles.Forsyth20060303a828770d19 years agoHistory
2050 lines · 49.3 KB · Brainfuck
RawDownload
implement CharonUtils; include "common.m";include "transport.m";include "date.m";include "translate.m"; 	date: Date;	me: CharonUtils;	sys: Sys;	D: Draw;	S: String;	U: Url;	T: StringIntTab; Font : import D;Parsedurl: import U;convcs : Convcs;trans : Translate;	Dict : import trans;dict : ref Dict; NCTimeout : con 100000;		# free NC slot after 100 secondsUBufsize : con 40*1024;		# initial buffer size for unknown lengthsUEBufsize : con 1024;		# initial buffer size for unknown lengths, error responses botchexception := "EXInternal: ByteSource protocol botch";bytesourceid := 0;crlf : con "\r\n";ctype : array of byte;	# local ref to C->ctype[]dbgproto : int;dbg: int;netconnid := 0;netconns := array[10] of ref Netconn;sptab : con " \t"; THTTP, TFTP, TFILE, TMAX: con iota;transports := array[TMAX] of Transport;tpaths := array [TMAX] of {	THTTP =>	Transport->HTTPPATH,	TFTP =>	Transport->FTPPATH,	TFILE =>	Transport->FILEPATH,}; schemes := array [] of {	("http", 	THTTP),	("https",	THTTP),	("ftp",	TFTP),	("file",	TFILE),}; ngchan : chan of (int, list of ref ByteSource, ref Netconn, chan of ref ByteSource); # must track HTTP methods in chutils.m# (upper-case, since that's required in HTTP requests)hmeth = array[] of { "GET", "POST" }; # following array must track media type def in chutils.m# keep in alphabetical ordermnames = array[] of {	"application/msword",	"application/octet-stream",	"application/pdf",	"application/postscript",	"application/rtf",	"application/vnd.framemaker",	"application/vnd.ms-excel",	"application/vnd.ms-powerpoint",	"application/x-unknown",	"audio/32kadpcm",	"audio/basic",	"image/cgm",	"image/g3fax",	"image/gif",	"image/ief",	"image/jpeg",	"image/png",	"image/tiff",	"image/x-bit",	"image/x-bit2",	"image/x-bitmulti",	"image/x-inferno-bit",	"image/x-xbitmap",	"model/vrml",	"multipart/digest",	"multipart/mixed",	"text/css",	"text/enriched",	"text/html",	"text/javascript",	"text/plain",	"text/richtext",	"text/sgml",	"text/tab-separated-values",	"text/xml",	"video/mpeg",	"video/quicktime"}; ncstatenames = array[] of {	"free", "idle", "connect", "gethdr", "getdata",	"done", "err"}; hsnames = array[] of {	"none", "information", "ok", "redirect", "request error", "server error"}; hcphrase(code: int) : string{	ans : string;	case code {	HCContinue =>				ans = X("Continue", "http");	HCSwitchProto =>			ans = X("Switching Protocols", "http");	HCOk =>					ans = X("Ok", "http");	HCCreated =>				ans = X("Created", "http");	HCAccepted =>				ans = X("Accepted", "http");	HCOkNonAuthoritative =>		ans = X("Non-Authoratative Information", "http");	HCNoContent =>			ans = X("No content", "http");	HCResetContent =>			ans = X("Reset content", "http");	HCPartialContent =>			ans = X("Partial content", "http");	HCMultipleChoices =>		ans = X("Multiple choices", "http");	HCMovedPerm =>			ans = X("Moved permanently", "http");	HCMovedTemp =>			ans = X("Moved temporarily", "http");	HCSeeOther =>				ans = X("See other", "http");	HCNotModified =>			ans = X("Not modified", "http");	HCUseProxy =>				ans = X("Use proxy", "http");	HCBadRequest =>			ans = X("Bad request", "http");	HCUnauthorized =>			ans = X("Unauthorized", "http");	HCPaymentRequired =>		ans = X("Payment required", "http");	HCForbidden =>			ans = X("Forbidden", "http");	HCNotFound =>			ans = X("Not found", "http");	HCMethodNotAllowed =>		ans = X("Method not allowed", "http");	HCNotAcceptable =>			ans = X("Not Acceptable", "http");	HCProxyAuthRequired =>		ans = X("Proxy authentication required", "http");	HCRequestTimeout =>		ans = X("Request timed-out", "http");	HCConflict =>				ans = X("Conflict", "http");	HCGone =>				ans = X("Gone", "http");	HCLengthRequired =>		ans = X("Length required", "http");	HCPreconditionFailed =>		ans = X("Precondition failed", "http");	HCRequestTooLarge =>		ans = X("Request entity too large", "http");	HCRequestURITooLarge =>	ans = X("Request-URI too large", "http");	HCUnsupportedMedia =>		ans = X("Unsupported media type", "http");	HCRangeInvalid =>			ans = X("Requested range not valid", "http");	HCExpectFailed =>			ans = X("Expectation failed", "http");	HCServerError =>			ans = X("Internal server error", "http");	HCNotImplemented =>		ans = X("Not implemented", "http");	HCBadGateway =>			ans = X("Bad gateway", "http");	HCServiceUnavailable =>		ans = X("Service unavailable", "http");	HCGatewayTimeout =>		ans = X("Gateway time-out", "http");	HCVersionUnsupported =>	ans = X("HTTP version not supported", "http");	HCRedirectionFailed =>		ans = X("Redirection failed", "http");	* =>						ans = X("Unknown code", "http");	}	return ans;} # This array should be kept sortedfileexttable := array[] of { T->StringInt	("ai", ApplPostscript),	("au", AudioBasic),# ("bit", ImageXBit),	("bit", ImageXInfernoBit),	("bit2", ImageXBit2),	("bitm", ImageXBitmulti),	("eps", ApplPostscript),	("gif", ImageGif),	("gz",	ApplOctets),	("htm", TextHtml),	("html", TextHtml),	("jpe", ImageJpeg),	("jpeg", ImageJpeg),	("jpg", ImageJpeg),	("pdf", ApplPdf),	("png", ImagePng),	("ps", ApplPostscript),	("shtml", TextHtml),	("text", TextPlain),	("tif", ImageTiff),	("tiff", ImageTiff),	("txt", TextPlain),	("zip", ApplOctets)}; # argl is command line# Return string that is empty if all ok, else path of module# that failed to load.init(ch: Charon, c: CharonUtils, argl: list of string, evc: chan of ref E->Event, cksrv: Cookiesrv, ckc: ref Cookiesrv->Client) : string{	me = c;	sys = load Sys Sys->PATH;	startres = ResourceState.cur();	D = load Draw Draw->PATH;	CH = ch;	S = load String String->PATH;	if(S == nil)		return String->PATH; 	U = load Url Url->PATH;	if(U == nil)		return Url->PATH;	U->init(); 	T = load StringIntTab StringIntTab->PATH;	if(T == nil)		return StringIntTab->PATH; 	trans = load Translate Translate->PATH;	if (trans != nil) {		trans->init();		(dict, nil) = trans->opendict(trans->mkdictname(nil, "charon"));	} 	# Now have all the modules needed to process command line	# (hereafter can use our loadpath() function to substitute the	# build directory version if dbg['u'] is set) 	setconfig(argl);	dbg = int config.dbg['d']; 	G = load Gui loadpath(Gui->PATH);	if(G == nil)		return loadpath(Gui->PATH); 	C = load Ctype loadpath(Ctype->PATH);	if(C == nil)		return loadpath(Ctype->PATH); 	E = load Events Events->PATH;	if(E == nil)		return loadpath(Events->PATH); 	J = load Script loadpath(Script->JSCRIPTPATH);	# don't report an error loading JavaScript, handled elsewhere 	LX = load Lex loadpath(Lex->PATH);	if(LX == nil)		return loadpath(Lex->PATH); 	B = load Build loadpath(Build->PATH);	if(B == nil)		return loadpath(Build->PATH); 	I = load Img loadpath(Img->PATH);	if(I == nil)		return loadpath(Img->PATH); 	L = load Layout loadpath(Layout->PATH);	if(L == nil)		return loadpath(Layout->PATH);	date = load Date loadpath(Date->PATH);	if (date == nil)		return loadpath(Date->PATH); 	convcs = load Convcs Convcs->PATH;	if (convcs == nil)		return loadpath(Convcs->PATH);  	# Intialize all modules after loading all, so that each	# may cache pointers to the other modules	# (G will be initialized in main charon routine, and L has to	# be inited after that, because it needs G's display to allocate fonts) 	E->init(evc);	I->init(me);	err := convcs->init(nil);	if (err != nil)		return err;	if(J != nil) {		err = J->init(me);		if (err != nil) {			# non-fatal: just don't handle javascript			J = nil;			if (dbg)				sys->print("%s\n", err);		}	}	B->init(me);	LX->init(me);	date->init(me); 	if (config.docookies) {		CK = cksrv;		ckclient = ckc;		if (CK == nil) {			path := loadpath(Cookiesrv->PATH);			CK = load Cookiesrv path;			if (CK == nil)				sys->print("cookies: cannot load server %s: %r\n", path);			else				ckclient = CK->start(config.userdir + "/cookies", 0);		}	} 	# preload some transports	gettransport("http");	gettransport("file"); 	progresschan = chan of (int, int, int, string);	imcache = ref ImageCache;	ctype = C->ctype;	dbgproto = int config.dbg['p'];	ngchan = chan of (int, list of ref ByteSource, ref Netconn, chan of ref ByteSource);	return "";} # like startreq() but special case for a string ByteSource# which doesn't need an associated netconnstringreq(s : string) : ref ByteSource{	bs := ByteSource.stringsource(s); 	G->progress <-= (bs.id, G->Pstart, 0, "text");	anschan := chan of ref ByteSource;	ngchan <-= (NGstartreq, bs :: nil, nil, anschan);	<-anschan;	return bs;} # Make a ByteSource for given request, and make sure# that it is on the queue of some Netconn.# If don't have a transport for the request's scheme,# the returned bs will have err set.startreq(req: ref ReqInfo) : ref ByteSource{	bs := ref ByteSource(			bytesourceid++,			req,		# req			nil,		# hdr			nil,		# data			0,		# edata			"",		# err			nil,		# net			1,		# refgo			1,		# refnc			0,		# eof			0,		# lim			0		# seenhdr		); 	G->progress <-= (bs.id, G->Pstart, 0, req.url.tostring());	anschan := chan of ref ByteSource;	ngchan <-= (NGstartreq, bs::nil, nil, anschan);	<-anschan;	return bs;} # Wait for some ByteSource in current go generation to# have a state change that goproc hasn't seen yet.waitreq(bsl: list of ref ByteSource) : ref ByteSource{	anschan := chan of ref ByteSource;	ngchan <-= (NGwaitreq, bsl, nil, anschan);	return <-anschan;} # Notify netget that goproc is finished with bs.freebs(bs: ref ByteSource){	anschan := chan of ref ByteSource;	ngchan <-= (NGfreebs, bs::nil, nil, anschan);	<-anschan;} abortgo(gopgrp: int){	if(int config.dbg['d'])		sys->print("abort go\n");	kill(gopgrp, 1);	freegoresources();	# renew the channels so that receives/sends by killed threads don't	# muck things up	ngchan = chan of (int, list of ref ByteSource, ref Netconn, chan of ref ByteSource);} freegoresources(){	for(i := 0; i < len netconns; i++) {		nc := netconns[i];		nc.makefree();	}} # This runs as a separate thread.# It acts as a monitor to synchronize access to the Netconn data# structures, as a dispatcher to start runnetconn's as needed to# process work on Netconn queues, and as a notifier to let goproc# know when any ByteSources have advanced their state.netget(){	msg, n, i: int;	bsl : list of ref ByteSource;	nc: ref Netconn;	waitix := 0;	c : chan of ref ByteSource;	waitpending : list of (list of ref ByteSource, chan of ref ByteSource);	maxconn := config.nthreads;	gncs := array[maxconn] of int; 	for(n = 0; n < len netconns; n++)		netconns[n] = Netconn.new(n); 	# capture netget chan to prevent abortgo() reset of	# ngchan from breaking us (channel hungup) before kill() does its job	ngc := ngchan;mainloop:	for(;;) {		(msg,bsl,nc,c) = <- ngc;		case msg {		NGstartreq =>			bs := hd bsl;			# bs has req filled in, and is otherwise in its initial state.			# Find a suitable Netconn and add bs to its queue of work,			# then send nil along c to let goproc continue. 			# if ReqInfo is nil then this is a string ByteSource			# in which case we don't need a netconn to service it as we have all			# data already			if (bs.req == nil) {				c <- = nil;				continue;			} 			if(dbgproto)				sys->print("Startreq BS=%d for %s\n", bs.id, bs.req.url.tostring());			scheme := bs.req.url.scheme;			host := bs.req.url.host;			(transport, err) := gettransport(scheme);			if(err != "")				bs.err = err;			else {				sport :=bs.req.url.port;				if(sport == "")					port := transport->defaultport(scheme);				else					port = int sport;				i = 0;				freen := -1;				for(n = 0; n < len netconns && (i < maxconn || freen == -1); n++) {					nc = netconns[n];					if(nc.state == NCfree) {						if(freen == -1)							freen = n;					}					else if(nc.host == host					   && nc.port == port && nc.scheme == scheme && i < maxconn) {						gncs[i++] = n;					}				}				if(i < maxconn) {					# use a new netconn for this bs					if(freen == -1) {						freen = len netconns;						newncs := array[freen+10] of ref Netconn;						newncs[0:] = netconns;						for(n = freen; n < freen+10; n++)							newncs[n] = Netconn.new(n);						netconns = newncs;					}					nc = netconns[freen];					nc.host = host;					nc.port = port;					nc.scheme = scheme;					nc.qlen = 0;					nc.ngcur = 0;					nc.gocur = 0;					nc.reqsent = 0;					nc.pipeline = 0;					nc.connected = 0;				}				else {					# use existing netconn with fewest outstanding requests					nc = netconns[gncs[0]];					if(maxconn > 1) {						minqlen := nc.qlen - nc.gocur;						for(i = 1; i < maxconn; i++) {							x := netconns[gncs[i]];							if(x.qlen-x.gocur < minqlen) {								nc = x;								minqlen = x.qlen-x.gocur;							}						}					}				}				if(nc.qlen == len nc.queue) {					nq := array[nc.qlen+10] of ref ByteSource;					nq[0:] = nc.queue;					nc.queue = nq;				}				nc.queue[nc.qlen++] = bs;				bs.net = nc;				if(dbgproto)					sys->print("Chose NC=%d for BS %d, qlen=%d\n", nc.id, bs.id, nc.qlen);				if(nc.state == NCfree || nc.state == NCidle) {					if(nc.connected) {						nc.state = NCgethdr;						if(dbgproto)							sys->print("NC %d: starting runnetconn in gethdr state\n", nc.id);					}

Lines 1–500 of 2050.