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. Load more
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 seconds UBufsize : con 40*1024; # initial buffer size for unknown lengths UEBufsize : 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 order mnames = 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 sorted fileexttable := 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 netconn stringreq(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); }