Syntax10.Scn.Fnt InfoElemsAllocUSyntax10.Scn.FntStampElemsAlloc6 Mar 980"Title": HTTP "Author": Andreas Krumenacker "Abstract": HTTP is a part of the Oberon-Web-Browser. "Keywords": HTTP, FILE, hypertext, internet-protocols "Version": no version "From": 15 Jan 98 "Until":  "Changes": no changes so far "Hints": no hints 8FoldElemsNewSyntax10.Scn.FntBalloonElemsAllocSyntax10.Scn.FntSyntax10i.Scn.Fnt+t'U*VA:+ M"HTTP" HTTP is a part of the Oberon-Web-Browser. "condFound" HTTP.condFound is a possible result, when the cache is checked for a file identified with a specific URL: HTTP.CheckCache (url : Web.Url; VAR res : INTEGER; VAR d, t : LONGINT) When condFound is returned in res, this means, that there is a file available in the cache but it might not be up to date. Other possible values of res are: HTTP.found HTTP.notFound "found" HTTP.found is a possible result, when the cache is checked for a file identified with a specific URL: HTTP.CheckCache (url : Web.Url; VAR res : INTEGER; VAR d, t : LONGINT) When found is returned in res, this means, that there is a file available in the cache. Other possible values of res are: HTTP.condFound HTTP.notFound "notFound" HTTP.notFound is a possible result, when the cache is checked for a file identified with a specific URL: HTTP.CheckCache (url : Web.Url; VAR res : INTEGER; VAR d, t : LONGINT) When notFound is returned in res, this means, that there is no file available in the cache. Other possible values of res are: HTTP.found HTTP.condFound "CachedFile" HTTP.CachedFile (url : Web.Url) : Web.FileInfo "CheckCache" HTTP.CheckCache (url : Web.Url; VAR res : INTEGER; VAR d, t : LONGINT) "PutToCache" HTTP.PutToCache (url : Web.Url; fi : Web.FileInfo; VAR dateTime : ARRAY OF CHAR) "Install" HTTP.Install "SetUserAndPw" HTTP.SetUserAndPw 'UserID:PassWord' 8^8Syntax10.Scn.FntSyntax10b.Scn.Fnt Syntax10i.Scn.Fnt [HYw found- = 0; condFound- = 1; notFound- = 2; (* results of checked cache *) defTimeOut = 600; (* 600 seconds = 10 minutes *) httpScheme = "http"; fileScheme = "file"; fileSchStarter = "file:///"; fileHost = "localhost"; protocol = "HTTP"; defSep = "/"; versionMajor = 1; versionMinor = 0; versionStr = "HTTP/1.0"; mGet = "GET"; mHead = "HEAD"; mPost = "POST"; defaultPort = 80; headItemLen = 512; stateLineLen = 64; bufLen = MAX(INTEGER) - 1; dateHead = "Date"; mimeHead = "Mime-Version"; pragmaHead = "Pragma"; locHead = "Location"; serverHead = "Server"; authHead = "WWW-Authenticate"; myAuthHead = "Authorization"; basic = "Basic"; allowHead = "Allow"; contCodeHead = "Content-Encoding"; contLenHead = "Content-Length"; contTypeHead = "Content-Type"; expHead = "Expires"; modHead = "Last-Modified"; condModHead = "If-Modified-Since"; clientStr = "User-Agent: Web/1.00 Oberon V4"; noCacheStr = "Pragma: no-cache"; acceptStr = "Accept: */*"; (* needed for some servers e.g. "Microsoft-Internet-Information-Server/1.0" *) Check = 0; Collect = 1; Complete = 2; All = 0; Left = 1; Right = 2; blank = 20X; 88Syntax10.Scn.FntM8FoldElemsNew#Syntax10.Scn.Fnt,, LoaderDesc = RECORD (Web.LoaderDesc) END;8*8#Syntax10.Scn.FntWW HeaderItemDesc = RECORD str : ARRAY headItemLen OF CHAR; next : HeaderItem; END;8<8#Syntax10.Scn.Fnt HeaderDesc = RECORD state : INTEGER; idx : LONGINT; item : HeaderItem; statusCode : INTEGER; reasonPhrase : ARRAY 64 OF CHAR; date : Date; mimeVersion : ARRAY 64 OF CHAR; pragma : ARRAY 128 OF CHAR; location : ARRAY 512 OF CHAR; server : ARRAY 128 OF CHAR; authenticate : ARRAY 128 OF CHAR; allow : ARRAY 32 OF CHAR; contentEncoding : Web.CodingId; contentLength : LONGINT; contentType : Web.ContentId; expires : Date; lastModified : Date; END;8&8#Syntax10.Scn.FntHH FileInfoDesc = RECORD (Web.FileInfoDesc) cont : Web.ContentId; END;88#Syntax10.Scn.Fnt TaskDesc = RECORD (Web.TaskDesc) act : Web.UrlStack; elem : Texts.Elem; timeOut : LONGINT; con : TCP.Connection; brokenConCount : SHORTINT; header : Header; dateTime : Date; useCache : BOOLEAN; auth : BOOLEAN; END;8 8#Syntax10.Scn.FntRR ProxyDesc = RECORD name : Web.Host; port : INTEGER; adr : TCP.IpAdr; END;8% Buffer = POINTER TO ARRAY bufLen OF CHAR; Loader = POINTER TO LoaderDesc;  HeaderItem = POINTER TO HeaderItemDesc;  Date = ARRAY 64 OF CHAR; Header = POINTER TO HeaderDesc;  FileInfo = POINTER TO FileInfoDesc;  Task = POINTER TO TaskDesc;  Proxy = POINTER TO ProxyDesc;  88Syntax10.Scn.FntSyntax10b.Scn.Fnt G / Syntax10i.Scn.Fnt CheckCache* : PROCEDURE (url : Web.Url; VAR res : INTEGER; VAR d, t : LONGINT); CachedFile* : PROCEDURE (url : Web.Url) : Web.FileInfo; PutToCache* : PROCEDURE (url : Web.Url; fi : Web.FileInfo; VAR dateTime : ARRAY OF CHAR); proxy : Proxy; waitForResponse : INTEGER; (* timeout delay in seconds *) buf : Buffer; r : Files.Rider; mDays : ARRAY 12 OF INTEGER; mName : ARRAY 12, 4 OF CHAR; dName : ARRAY 7, 4 OF CHAR; userPw : ARRAY 128 OF CHAR; 8Mh8#Syntax10.Scn.Fntvv VAR newCk : Web.ContentKey; BEGIN NEW(newCk); newCk.alt := ck; newCk.id := id; ck := newCk END PrependContentKey;8C$8#Syntax10.Scn.Fnt VAR len, i, j : INTEGER; BEGIN len := Strings.Length(str); CASE which OF All : i := 0; j := 0; WHILE i < len DO IF str[i] # blank THEN str[j] := str[i]; INC(j) END; INC(i); END; str[j] := 0X; | Left : i := 0; WHILE (i < len) & (str[i] = blank) DO INC(i) END; Strings.Delete(str, 0, i); | Right : i := len - 1; WHILE (i >= 0) & (str[i] = blank) DO DEC(i) END; str[i + 1] := 0X; END END EatBlanks;8Q8#Syntax10.Scn.Fnt   VAR pos : INTEGER; BEGIN pos := Strings.Pos(sep, str, 0); IF pos > 0 THEN Strings.Extract(str, 0, pos, split); Strings.Delete(str, 0, pos + Strings.Length(sep)); EatBlanks(split, Right); EatBlanks(str, Left) ELSE split[0] := 0X; END END SplitStringAt;8;I8#Syntax10.Scn.Fnt VAR i : INTEGER; BEGIN i := 0; WHILE (CAP(s1[i]) = CAP(s2[i])) & (s1[i] # 0X) DO INC(i) END; RETURN (s1[i] = 0X) & (s2[i] = 0X) END IsSameName;8C8QSyntax10.Scn.FnthSyntax10i.Scn.Fntd#% CONST CharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; Pad = 3DX; (* "=" *) VAR len, i, j, sIdx : INTEGER; sNum : ARRAY 3 OF INTEGER; charSet : ARRAY 65 OF CHAR; (* indexed object has to be a variable *) BEGIN len := Strings.Length(s) - 1; j := 0; IF (len DIV 3 + 1) * 4 <= LEN(b) THEN charSet := CharSet; i := 0; sIdx := 0; WHILE i <= len DO sNum[sIdx] := ORD(s[i]); INC(i); INC(sIdx); IF sIdx = 3 THEN sIdx := 0; b[j] := charSet[sNum[0] DIV 4]; b[j + 1] := charSet[(sNum[0] MOD 4) * 16 + sNum[1] DIV 16]; b[j + 2] := charSet[(sNum[1] MOD 16) * 4 + sNum[2] DIV 64]; b[j + 3] := charSet[sNum[2] MOD 64]; INC(j, 4); END; END; IF sIdx > 0 THEN b[j] := charSet[sNum[0] DIV 4]; IF sIdx > 1 THEN b[j + 1] := charSet[(sNum[0] MOD 4) * 16 + sNum[1] DIV 16]; b[j + 2] := charSet[(sNum[1] MOD 16) * 4]; ELSE b[j + 1] := charSet[(sNum[0] MOD 4) * 16]; b[j + 2] := Pad END; b[j + 3] := Pad; INC(j, 4) END END; b[j] := 0X END Str2Base64;8=a8Syntax10.Scn.FntSyntax10i.Scn.Fnt-n8FoldElemsNewCSyntax10.Scn.FntSyntax10i.Scn.FntPP (* compute days since 1.1.1950, which was a sunday, in ds50 and return the dayindex *) VAR ds50, i : INTEGER; BEGIN ds50 := (y - 1950) * 365 + mDays[m - 1] + d - 1; IF m <= 2 THEN DEC(y) END; FOR i := 1952 TO y BY 4 DO IF (y MOD 100 # 0) OR (y MOD 400 = 0) THEN INC(ds50) END END; RETURN ds50 MOD 7 END WeekDayIdx;83a8#Syntax10.Scn.Fnt}} BEGIN s[i] := CHR(ORD("0") + n DIV 10); s[i+1] := CHR(ORD("0") + n MOD 10); s[i+2] := sep; INC(i, 3) END AppendPair;8 (* format: "n(ddd), dd n(mmm) yyyy hh:mm:ss GMT" *) CONST timeSep = 3AX; VAR day, month, year, hour, min, sec, i : INTEGER; PROCEDURE WeekDayIdx (y, m, d : INTEGER) : INTEGER;  PROCEDURE AppendPair (n : INTEGER; sep : CHAR);  BEGIN IF d = 0 THEN Web.GetGMT(t, d) END; year := SHORT(d DIV 512 MOD 128); IF year < 50 THEN INC(year, 2000) ELSE INC(year, 1900) END; month := SHORT(d DIV 32 MOD 16); day := SHORT(d MOD 32); hour := SHORT(t DIV 4096 MOD 32); min := SHORT(t DIV 64 MOD 64); sec := SHORT(t MOD 64); COPY(dName[WeekDayIdx(year, month, day)], s); s[3] := ","; s[4] := blank; i := 5; AppendPair(day, blank); s[i] := mName[month-1, 0]; s[i+1] := mName[month-1, 1]; s[i+2] := mName[month-1, 2]; s[i+3] := blank; INC(i, 4); AppendPair(year DIV 100, 0X); DEC(i); AppendPair(year MOD 100, blank); AppendPair(hour, timeSep); AppendPair(min, timeSep); AppendPair(sec, blank); s[i] := "G"; s[i+1] := "M"; s[i+2] := "T"; s[i+3] := 0X END DateStr;88#Syntax10.Scn.Fnt%% VAR head : Header; BEGIN NEW(head); head.state := Check; head.idx := 0; NEW(head.item); head.item.str[0] := 0X; head.item.next := NIL; head.statusCode := 0; head.reasonPhrase[0] := 0X; head.date[0] := 0X; head.mimeVersion[0] := 0X; head.pragma[0] := 0X; head.location[0] := 0X; head.server[0] := 0X; head.authenticate[0] := 0X; head.allow[0] := 0X; head.contentEncoding[0] := 0X; head.contentLength := Web.undefinedLen; head.contentType[0] := 0X; head.expires[0] := 0X; head.lastModified[0] := 0X; RETURN head END NewHeader;8(n8#Syntax10.Scn.Fntpp BEGIN Web.LogF("HTTP-state: # ", h.statusCode); Web.LogStr(h.reasonPhrase); Web.LogStr("$") END ShowStateLine;8%\8#Syntax10.Scn.Fnt BEGIN ShowStateLine(h); IF h.date # "" THEN Web.LogStr(dateHead); Web.LogStr(": "); Web.LogStr(h.date); Web.LogStr("$") END; IF h.mimeVersion # "" THEN Web.LogStr(mimeHead); Web.LogStr(": "); Web.LogStr(h.mimeVersion); Web.LogStr("$") END; IF h.pragma # "" THEN Web.LogStr(pragmaHead); Web.LogStr(": "); Web.LogStr(h.pragma); Web.LogStr("$") END; IF h.location # "" THEN Web.LogStr(locHead); Web.LogStr(": "); Web.LogStr(h.location); Web.LogStr("$") END; IF h.server # "" THEN Web.LogStr(serverHead); Web.LogStr(": "); Web.LogStr(h.server); Web.LogStr("$") END; IF h.authenticate # "" THEN Web.LogStr(authHead); Web.LogStr(": "); Web.LogStr(h.authenticate); Web.LogStr("$") END; IF h.allow # "" THEN Web.LogStr(allowHead); Web.LogStr(": "); Web.LogStr(h.allow); Web.LogStr("$") END; IF h.contentEncoding # "" THEN Web.LogStr(contCodeHead); Web.LogStr(": "); Web.LogStr(h.contentEncoding); Web.LogStr("$") END; IF h.contentLength # Web.undefinedLen THEN Web.LogStr(contLenHead); Web.LogF(": #$", h.contentLength) END; IF h.contentType # "" THEN Web.LogStr(contTypeHead); Web.LogStr(": "); Web.LogStr(h.contentType); Web.LogStr("$") END; IF h.expires # "" THEN Web.LogStr(expHead); Web.LogStr(": "); Web.LogStr(h.expires); Web.LogStr("$") END; IF h.lastModified # "" THEN Web.LogStr(modHead); Web.LogStr(": "); Web.LogStr(h.lastModified); Web.LogStr("$") END END ShowHeader;8+8Syntax10.Scn.FntSyntax10i.Scn.Fnt>ls8FoldElemsNew#Syntax10.Scn.Fntkk BEGIN WHILE (ch = blank) OR (ch = 09X) OR (ch = 0DX) DO INC(i); ch := h.item.str[i] END END IgnoreLWS;848#Syntax10.Scn.Fnt BEGIN j := 0; WHILE (ch >= "0") & (ch <= "9") DO str[j] := ch; INC(i); INC(j); ch := h.item.str[i] END; str[j] := 0X; int := SHORT(Web.Str2Int(str)); RETURN j > 0 END Scanned;8N4 (* Check for "HTTP/ LWS #.# LWS Staus-Code LWS Reason-Phrase LWS" *) VAR ch : CHAR; str : ARRAY 16 OF CHAR; i, j, len, major, minor : INTEGER; PROCEDURE IgnoreLWS;  PROCEDURE Scanned (VAR int : INTEGER) : BOOLEAN;  BEGIN h.state := Complete; len := Strings.Length(h.item.str); i := 0; j := 0; ch := h.item.str[i]; WHILE (ch # blank) & (ch # defSep) & (ch # 09X) & (ch # 0DX) & (j < 5) & (i < len) DO str[j] := ch; INC(i); INC(j); ch := h.item.str[i] END; str[j] := 0X; IF (ch = defSep) & (str = protocol) THEN INC(i); j := 0; ch := h.item.str[i]; IgnoreLWS; IF Scanned(major) & (major >= versionMajor) & (ch = ".") THEN INC(i); ch := h.item.str[i]; IF Scanned(minor) & ((minor >= versionMinor) OR (major > versionMajor)) THEN IgnoreLWS; IF Scanned(h.statusCode) THEN IgnoreLWS; j := 0; WHILE i <= len DO IF (ch # 09X) & (ch # 0DX) THEN h.reasonPhrase[j] := ch; INC(j) END; INC(i); ch := h.item.str[i] END; h.state := Collect; h.item := NIL; END END END END END AnalizeStateLine;8&8#Syntax10.Scn.Fnt55 VAR firstWord : ARRAY 64 OF CHAR; BEGIN WHILE h.item # NIL DO SplitStringAt(":", firstWord, h.item.str); IF firstWord[0] # 0X THEN IF IsSameName(firstWord, dateHead) THEN COPY(h.item.str, h.date) ELSIF IsSameName(firstWord, mimeHead) THEN COPY(h.item.str, h.mimeVersion) ELSIF IsSameName(firstWord, pragmaHead) THEN COPY(h.item.str, h.pragma) ELSIF IsSameName(firstWord, locHead) THEN COPY(h.item.str, h.location) ELSIF IsSameName(firstWord, serverHead) THEN COPY(h.item.str, h.server) ELSIF IsSameName(firstWord, authHead) THEN COPY(h.item.str, h.authenticate) ELSIF IsSameName(firstWord, allowHead) THEN COPY(h.item.str, h.allow) ELSIF IsSameName(firstWord, contCodeHead) THEN COPY(h.item.str, h.contentEncoding) ELSIF IsSameName(firstWord, contLenHead) THEN h.contentLength := Web.Str2Int(h.item.str); ELSIF IsSameName(firstWord, contTypeHead) THEN SplitStringAt(";", firstWord, h.item.str); IF firstWord[0] # 0X THEN COPY(firstWord, h.contentType) ELSE COPY(h.item.str, h.contentType) END; EatBlanks(h.contentType, All) ELSIF IsSameName(firstWord, expHead) THEN COPY(h.item.str, h.expires) ELSIF IsSameName(firstWord, modHead) THEN COPY(h.item.str, h.lastModified) END END; h.item := h.item.next END END SplitHeader;8;8CSyntax10.Scn.FntSyntax10i.Scn.Fnt'2 CONST infoClass = 1; ok = 200; noContent = 204; redirClass = 3; notModified = 304; VAR code, cClass : INTEGER; check : ARRAY 16 OF CHAR; BEGIN break := FALSE; code := t.header.statusCode; cClass := code DIV 100; IF (t.act.meth = mHead) OR (code = noContent) OR (cClass = infoClass) THEN break := TRUE; ShowHeader(t.header) ELSIF code # ok THEN IF (t.header.authenticate # "") & ~ t.auth & (userPw # "") THEN Strings.Extract(t.header.authenticate, 0, Strings.Length(basic), check); IF IsSameName(check, basic) THEN break := TRUE; HttpOpen(t.act, t.txt, t.elem, FALSE, TRUE) END ELSIF code = notModified THEN break := TRUE; Web.OpenPresentator(CachedFile(t.act.url), t.act, t.txt, t.elem) ELSE ShowStateLine(t.header); IF (cClass = redirClass) & (t.header.location # "") THEN IF t.act.meth = mPost THEN break := (t.header.contentLength = 0) OR ((t.header.contentLength = Web.undefinedLen) & (Files.Length(Files.Base(t.fi.r)) = 0)); Web.LogStr("HTTP_"); Web.LogStr(mPost); Web.LogStr(" Redirected to:$"); Web.LogStr(t.header.location); Web.LogStr("$"); ELSE break := TRUE; t.act.url := Web.Str2Url(t.header.location); HttpOpen(t.act, t.txt, t.elem, t.useCache, t.auth) END END END END; IF ~ break THEN t.fi.len.def := t.header.contentLength; t.fi.cKey := NIL; PrependContentKey(t.header.contentType, t.fi.cKey); IF t.fi.cKey.id = "" THEN Web.GetFileExtension(t.fi.cKey.id, t.act.url) END; t.fi(FileInfo).cont := t.fi.cKey.id; t.fi.coding := t.header.contentEncoding END; IF code = ok THEN IF t.header.lastModified # "" THEN COPY(t.header.lastModified, t.dateTime) ELSIF t.header.date # "" THEN COPY(t.header.date, t.dateTime) END ELSE t.dateTime := "" END; (* no date - no caching *) t.header := NIL END AnalizeHeader;8l8{Syntax10.Scn.Fnt[Syntax10i.Scn.Fnt.#H2%U VAR len, i, j : LONGINT; item : HeaderItem; BEGIN WHILE avl > 0 DO IF (avl > exp) & (exp >= 0) THEN avl := exp END; IF avl > bufLen THEN len := bufLen ELSE len := avl END; TCP.ReadBytes(con, buf^, 0, len); IF (head # NIL) & ((head.state = Check) OR (head.state = Collect)) THEN i := 0; WHILE i < len DO IF head.state = Check THEN IF (buf[i] # 0AX) & (head.idx < stateLineLen) THEN head.item.str[head.idx] := buf[i]; INC(head.idx) ELSE head.item.str[head.idx] := 0X; head.idx := 0; AnalizeStateLine(head); IF head.state # Collect THEN (* no HTTP/1.0 header => assert HTTP/0.9 response *) Files.WriteBytes(r, head.item.str, Strings.Length(head.item.str)); head.item := NIL; FOR j := i TO len - 1 DO Files.Write(r, buf[j]) END; i := len (* exit while loop *) END END ELSE (* head.state = Collect *) IF (buf[i] # 09X) & (buf[i] # 0DX) THEN IF buf[i] = 0AX THEN IF head.idx = 0 THEN head.state := Complete; SplitHeader(head); FOR j := i + 1 TO len - 1 DO Files.Write(r, buf[j]) END; IF head.contentLength # Web.undefinedLen THEN exp := head.contentLength + i + 1 (* include DEC(exp, len) at the end of the outer loop *) END; i := len (* exit while loop *) ELSE head.item.str[head.idx] := 0X; head.idx := 0 END ELSE IF head.idx >= headItemLen - 1 THEN head.item.str[head.idx] := 0X; head.idx := 0 END; IF head.idx = 0 THEN NEW(item); item.next := head.item; head.item := item; item.str[0] := 0X END; head.item.str[head.idx] := buf[i]; INC(head.idx) END END END; INC(i) END ELSE Files.WriteBytes(r, buf^, len) END; DEC(avl, len); DEC(exp, len); IF (exp > 0) & (avl < 1) THEN avl := TCP.Available(con) END; END END ReceiveResponse;8 8CSyntax10.Scn.FntSyntax10i.Scn.Fnt+ VAR thisTask : Task; thisFile : Files.File; avl, exp : LONGINT; done : BOOLEAN; BEGIN thisTask := Oberon.CurTask(Task); thisFile := Files.Base(thisTask.fi.r); done := TRUE; IF (thisTask.con = NIL) OR ~ TCP.Connected(thisTask.con) THEN IF thisTask.fi.len.def > Files.Length(thisFile) THEN IF thisTask.brokenConCount > 1 THEN Web.LogStr("HTTP broken connection$") ELSE INC(thisTask.brokenConCount); done := thisTask.cancelled END (* give presentator a chance to report success *) END ELSIF thisTask.timeOut < Oberon.Time() THEN Web.LogStr("HTTP: timeout$") ELSE done := thisTask.cancelled OR (thisTask.fi.len.def = 0) END; IF ~ done & (thisTask.brokenConCount < 2) THEN avl := TCP.Available(thisTask.con); exp := thisTask.fi.len.def - Files.Length(thisFile); IF avl > 0 THEN ReceiveResponse(thisTask.con, thisTask.fi.r, avl, exp, thisTask.header); IF (thisTask.header # NIL) & (thisTask.header.state = Complete) THEN AnalizeHeader(thisTask, done); IF ~ done THEN Web.OpenPresentator(thisTask.fi, thisTask.act, thisTask.txt, thisTask.elem) END END; thisTask.timeOut := Oberon.Time() + waitForResponse * Input.TimeUnit END; Files.Close(thisFile); done := done OR (thisTask.fi.len.def <= Files.Length(thisFile)) END; IF done THEN Files.Close(thisFile); thisTask.fi.len.act := Files.Length(thisFile); IF (thisTask.fi.len.def > 0) & (thisTask.fi.len.def <= thisTask.fi.len.act) & (thisTask.dateTime # "") & (thisTask.act.meth = mGet) THEN IF (thisTask.fi # NIL) & (thisTask.fi.cKey # NIL) & (thisTask.fi IS FileInfo) & (thisTask.fi.cKey.id # thisTask.fi(FileInfo).cont) THEN PrependContentKey(thisTask.fi(FileInfo).cont, thisTask.fi.cKey) END; PutToCache(thisTask.act.url, thisTask.fi, thisTask.dateTime) END; IF (thisTask.con # NIL) & TCP.Connected(thisTask.con) THEN TCP.Disconnect(thisTask.con) END; Web.RemoveTask(thisTask); Oberon.Remove(thisTask) END END GetResponse;8G8#Syntax10.Scn.Fnt VAR res : INTEGER; BEGIN IF url.net.hostType = Web.Name THEN TCP.HostByName(url.net.host, adr, res) ELSIF url.net.hostType = Web.Number THEN TCP.HostByNumber(url.net.host, adr, res) ELSE res := TCP.NotDone END; RETURN res = TCP.Done; END HostAdrSet;8h8 Syntax10.Scn.Fnt8FoldElemsNew#Syntax10.Scn.Fnt VAR ok : BOOLEAN; BEGIN ok := TRUE; IF (url.net.hostType = Web.Empty) OR (url.net.hostType = Web.Omitted) THEN Web.LogStr("HTTP: missing host$"); ok := FALSE ELSIF url.net.hostType = Web.Illegal THEN Web.LogStr('HTTP: illegal host "'); Web.LogStr(url.net.host); Web.LogStr('"$'); ok := FALSE END; IF (url.path[0] # defSep) & (url.path[0] # 0X) THEN Web.LogStr("HTTP: illegal path$"); ok := FALSE END; RETURN ok; END UrlChecked;8Ua8Syntax10.Scn.FntSyntax10i.Scn.Fnt/Syntax8i.Scn.Fnt -!6  (* Full-Request = Request-Line {(General-Header | Request-Header | Entity-Header) CRLF } CRLF [Entity-Body]. Request-Line = Method SP Request-URL SP HTTP-Version CRLF. General-Header = Date | MIME-Version | Pragma. Request-Header = Authorzation | From | If-Modified-Since | Referer | User-Agent. Entity-Header = Allow | Content-Encoding | Content-Length | Content-Type | Expires | Last-Modified | extension-header. *) CONST root = "/"; sep = ": "; VAR dateStr : Date; bodyLen : ARRAY 16 OF CHAR; CrLf : ARRAY 3 OF CHAR; BEGIN CrLf[0] := 0DX; CrLf[1] := 0AX; CrLf[2] := 0X; Web.GetRequestStr(act.url, forProxy, rStr); IF rStr[0] = 0X THEN COPY(root, rStr) END; IF forProxy OR (rStr[0] = root) THEN Strings.Insert(blank, 0, rStr); Strings.Insert(act.meth, 0, rStr); Strings.Append(blank, rStr); Strings.Append(versionStr, rStr); Strings.Append(CrLf, rStr); IF act.meth = mPost THEN DateStr(0, 0, dateStr); (* 0, 0 means now *) Strings.Append(dateHead, rStr); Strings.Append(sep, rStr); Strings.Append(dateStr, rStr); Strings.Append(CrLf, rStr); END; IF ~ useCache THEN Strings.Append(noCacheStr, rStr); Strings.Append(CrLf, rStr) END; IF auth & (userPw # "") THEN Strings.Append(myAuthHead, rStr); Strings.Append(sep, rStr); Strings.Append(basic, rStr); Strings.Append(blank, rStr); Strings.Append(userPw, rStr); Strings.Append(CrLf, rStr); END; IF date > 0 THEN DateStr(date, time, dateStr); Strings.Append(condModHead, rStr); Strings.Append(sep, rStr); Strings.Append(dateStr, rStr); Strings.Append(CrLf, rStr) END; Strings.Append(acceptStr, rStr); Strings.Append(CrLf, rStr); Strings.Append(clientStr, rStr); Strings.Append(CrLf, rStr); IF act.meth = mPost THEN IF (act.body # NIL) & (act.body.file # NIL) THEN Web.Int2Str(Files.Length(act.body.file), bodyLen); IF act.body.type # "" THEN Strings.Append(contTypeHead, rStr); Strings.Append(sep, rStr); Strings.Append(act.body.type, rStr); Strings.Append(CrLf, rStr) END; IF act.body.coding # "" THEN Strings.Append(contCodeHead, rStr); Strings.Append(sep, rStr); Strings.Append(act.body.coding, rStr); Strings.Append(CrLf, rStr) END; ELSE bodyLen := "0" END; Strings.Append(contLenHead, rStr); Strings.Append(sep, rStr); Strings.Append(bodyLen, rStr); Strings.Append(CrLf, rStr) END; Strings.Append(CrLf, rStr); RETURN TRUE ELSE RETURN FALSE END END RequestStrSet;8R8QSyntax10.Scn.FntSyntax10i.Scn.Fnt+5 CONST bLen = 2048; VAR hostAdr : TCP.IpAdr; port : INTEGER; buf : ARRAY bLen OF CHAR; BEGIN res := TCP.NotDone; IF (proxy # NIL) & RequestStrSet(TRUE, buf) THEN NEW(hostCon); TCP.Connect(hostCon, TCP.AnyPort, proxy.adr, proxy.port, 0, res); IF (res # TCP.Done) OR ~ TCP.Connected(hostCon) THEN Web.LogStr('HTTP: could not connect to proxy "'); Web.LogStr(proxy.name); Web.LogStr('"$'); res := TCP.NotDone END END; IF res = TCP.NotDone THEN IF HostAdrSet(hostAdr, act.url) & RequestStrSet(FALSE, buf) THEN port := act.url.net.port; IF port = Web.anyPort THEN port := defaultPort END; NEW(hostCon); TCP.Connect(hostCon, TCP.AnyPort, hostAdr, port, 0, res); IF (res # TCP.Done) OR ~ TCP.Connected(hostCon) THEN Web.LogStr('HTTP: could not connect to host "'); Web.LogStr(act.url.net.host); Web.LogStr('"$'); res := TCP.NotDone END ELSE Web.LogStr('HTTP: the host "'); Web.LogStr(act.url.net.host); Web.LogStr('" does not have a DNS entry$') END END; IF res = TCP.Done THEN TCP.WriteBytes(hostCon, buf, 0, Strings.Length(buf)); IF (TCP.res = TCP.Done) & (act.body # NIL) & (act.body.file # NIL) THEN Files.Set(r, act.body.file, 0); WHILE (TCP.res = TCP.Done) & ~ r.eof DO Files.ReadBytes(r, buf, bLen); TCP.WriteBytes(hostCon, buf, 0, bLen - r.res); TCP.Write(hostCon, 0DX); TCP.Write(hostCon, 0AX) (* termination of body needed for some servers *) END END; IF TCP.res # TCP.Done THEN (* could not send request *) TCP.Disconnect(hostCon); res := TCP.NotDone; Web.LogStr("HTTP: could not send request$") END END END ConnectAndRequest;8,8#Syntax10.Scn.Fnt VAR task : Task; fi : FileInfo; BEGIN NEW(task); task.safe := FALSE; task.time := 0; task.handle := GetResponse; task.txt := txt; NEW(fi); task.fi := fi; Files.Set(task.fi.r, file, 0); NEW(task.fi.len); task.fi.len.def := Web.undefinedLen; task.fi.len.act := -1; task.fi.local := FALSE; task.fi.cKey := NIL; fi.cont[0] := 0X; task.act := act; task.elem := elem; task.timeOut := Oberon.Time() + waitForResponse * Input.TimeUnit; task.con := con; task.brokenConCount := 0; task.header := NewHeader(); DateStr(0, 0, task.dateTime); task.useCache := useCache; task.auth := auth; Web.AddTask(task); Oberon.Install(task) END InstallTask;8 VAR result : INTEGER; date, time : LONGINT; con : TCP.Connection; file : Files.File; frame : TextFrames.Frame; PROCEDURE UrlChecked (url : Web.Url) : BOOLEAN;  PROCEDURE RequestStrSet (forProxy : BOOLEAN; VAR rStr : ARRAY OF CHAR) : BOOLEAN;  PROCEDURE ConnectAndRequest (VAR hostCon : TCP.Connection; VAR res : INTEGER);  PROCEDURE InstallTask;  BEGIN IF UrlChecked(act.url) THEN IF act.meth = "" THEN act.meth := mGet END; IF act.meth # mPost THEN act.body := NIL END; IF useCache & (act.meth = mGet) THEN CheckCache(act.url, result, date, time) ELSE result := notFound END; IF result = found THEN Web.OpenPresentator(CachedFile(act.url), act, txt, elem) ELSE IF result = notFound THEN date := 0 END; Web.GetFrame(frame, txt); IF frame # NIL THEN TextFrames.Mark(frame, -1) END; ConnectAndRequest(con, result); IF (result = TCP.Done) & TCP.Connected(con) THEN file := Files.New(""); InstallTask END; IF frame # NIL THEN TextFrames.Mark(frame, 0) END END END END HttpOpen;8Syntax10i.Scn.Fnt+Dt8#Syntax10.Scn.Fntjj VAR top : BOOLEAN; BEGIN top := Strings.Pos(defSep, str, 1) = Strings.Length(str) - 1; IF Web.StrDecoded(str) THEN END; Files.Set(r, f, 0); Files.WriteString(r, "Directory"); Files.Write(r, 0DX); Files.Write(r, 0AX); Files.WriteString(r, "

Directory of "); Files.WriteString(r, str); Files.WriteString(r, "

"); Files.Write(r, 0DX); Files.Write(r, 0AX); Files.WriteString(r, "
");
	IF ~ top THEN Files.WriteString(r, 'Up to higher level directory') END;
	Files.Write(r, 0DX); Files.Write(r, 0AX);
	Files.WriteString(r, "
"); Files.Close(f) END CloseDirFile;8z8#Syntax10.Scn.Fnt VAR codedName : ARRAY 256 OF CHAR; i : INTEGER; BEGIN COPY(name, codedName); Web.EncodeStr(codedName, ""); Files.WriteString(r, '
  • '); Files.WriteString(r, name); Files.WriteString(r, ""); IF isDir THEN FOR i := Strings.Length(name) TO 39 DO Files.Write(r, blank) END; Files.WriteString(r, " directory") END; Files.Write(r, 0DX); Files.Write(r, 0AX); continue := TRUE END CreateDirFileList;8N8#Syntax10.Scn.FntKK VAR i : INTEGER; file : Files.File; fi : Web.FileInfo; path : Web.Path; dir : Directories.Directory; cont : Web.ContentKey; BEGIN cont := NIL; IF (act.meth = "") OR (act.meth = mGet) THEN act.body := NIL; IF (act.url.net.netLocType = Web.Empty) OR IsSameName(act.url.net.host, fileHost) THEN IF act.url.path[0] = defSep THEN i := 0; WHILE act.url.path[i+1] # 0X DO IF act.url.path[i+1] = defSep THEN path[i] := Directories.delimiter ELSE path[i] := act.url.path[i+1] END; INC(i) END; path[i] := 0X; IF i > 0 THEN IF Web.StrDecoded(path) THEN IF path[i-1] # Directories.delimiter THEN path[i] := Directories.delimiter; path[i+1] := 0X END; dir := Directories.This(path); IF dir # NIL THEN file := Files.New(""); IF act.url.path[i] # defSep THEN act.url.path[i+1] := defSep; act.url.path[i+2] := 0X END; InitDirFile(file, act.url.path); Directories.Enumerate(dir, CreateDirFileList); CloseDirFile(file); PrependContentKey("text/html", cont) ELSE IF path[i-1] = Directories.delimiter THEN path[i-1] := 0X ELSE path[i] := 0X END; file := Files.Old(path); IF file # NIL THEN IF act.url.path[i] = defSep THEN act.url.path[i] := 0X END; IF elem = NIL THEN PrependContentKey(Web.wildCard, cont) END; PrependContentKey("", cont); Web.GetFileExtension(cont.id, act.url); IF cont.id = "" THEN cont := cont.alt END ELSE Web.LogStr("HTTP/FILE: file or directory not found$") END END; IF file # NIL THEN NEW(fi); Files.Set(fi.r, file, 0); NEW(fi.len); fi.len.def := Files.Length(file); fi.len.act := fi.len.def; fi.local := TRUE; fi.cKey := cont; fi.coding := ""; Web.OpenPresentator(fi, act, txt, elem) END ELSE Web.LogStr("HTTP/FILE: illegal path$") END END ELSE Web.LogStr("HTTP/FILE: no abs path$") END ELSE Web.LogStr("HTTP/FILE: illegal host$") END ELSE Web.LogStr("HTTP/FILE: illegal request method$") END END FileOpen;83s8#Syntax10.Scn.Fntkk VAR url : ARRAY 512 OF CHAR; i, j : INTEGER; BEGIN COPY(fileSchStarter, url); Web.EncodeStr(path, Directories.delimiter); i := - 1; j := Strings.Length(url); REPEAT INC(i); IF path[i] = Directories.delimiter THEN url[i+j] := defSep ELSE url[i+j] := path[i] END UNTIL path[i] = 0X; Web.OpenUrl("", url, NIL, NIL, NIL, Web.default) END OpenOberonFile;8k8QSyntax10.Scn.Fnt[Syntax10i.Scn.Fnt  - BEGIN IF ldr.scheme = httpScheme THEN HttpOpen(act, txt, elem, cached, FALSE) ELSE (* ldr.scheme = fileScheme *) FileOpen(act, txt, elem) END END Open;8 Syntax10b.Scn.Fnt u8#Syntax10.Scn.Fntii VAR str : ARRAY 64 OF CHAR; BEGIN In.Open; In.String(str); Str2Base64(str, userPw) END SetUserAndPw;8R8#Syntax10.Scn.Fnt%% BEGIN res := notFound END NotFound;8n8CSyntax10.Scn.Fnt Syntax10i.Scn.Fnt$> BEGIN (* only used to force loading of module *) END Install;88#Syntax10.Scn.Fnt// VAR ldr : Loader; BEGIN NEW(ldr); ldr.scheme := httpScheme; ldr.defPort := 80; ldr.infoMeth := mHead; Web.InstallLoader(ldr); NEW(ldr); ldr.scheme := fileScheme; ldr.defPort := Web.anyPort; ldr.infoMeth := ""; Web.InstallLoader(ldr); Web.fileOpener := OpenOberonFile END InstallProtocols;88#Syntax10.Scn.FntAA CONST netLocSep = "//"; VAR prof : Web.ProfileEntry; url : Web.Url; BEGIN CheckCache := NotFound; CachedFile := NIL; PutToCache := NoCache; mDays[0] := 0; mDays[1] := 31; mDays[2] := 59; mDays[3] := 90; mDays[4] := 120; mDays[5] := 151; mDays[6] := 181; mDays[7] := 212; mDays[8] := 243; mDays[9] := 273; mDays[10] := 304; mDays[11] := 334; mName[0] := "Jan"; mName[1] := "Feb"; mName[2] := "Mar"; mName[3] := "Apr"; mName[4] := "May"; mName[5] := "Jun"; mName[6] := "Jul"; mName[7] := "Aug"; mName[8] := "Sep"; mName[9] := "Oct"; mName[10] := "Nov"; mName[11] := "Dec"; dName[0] := "Sun"; dName[1] := "Mon"; dName[2] := "Tue"; dName[3] := "Wed"; dName[4] := "Thu"; dName[5] := "Fri"; dName[6] := "Sat"; proxy := NIL; Web.GetProfile("HTTP", "timeOut", prof); IF (prof # NIL) & (prof.class = Texts.Int) THEN waitForResponse := SHORT(prof.int) ELSE waitForResponse := defTimeOut END; Web.GetProfile("HTTP", "proxy", prof); proxy := NIL; IF (prof # NIL) & (prof.class IN {Texts.Name, Texts.String}) THEN IF Strings.Pos (netLocSep, prof.str, 0) < 0 THEN Strings.Insert (netLocSep, 0, prof.str) END; url := Web.Str2Url(prof.str); IF (url # NIL) & (url.net.hostType IN {Web.Name, Web.Number}) THEN NEW(proxy); IF HostAdrSet(proxy.adr, url) THEN proxy.name := url.net.host; proxy.port := url.net.port; IF proxy.port = Web.anyPort THEN proxy.port := defaultPort END ELSE proxy := NIL; Web.LogStr('HTTP: the proxy "'); Web.LogStr(url.net.host); Web.LogStr('" does not have a DNS entry$') END END END; userPw[0] := 0X END InitGlobalVars;88#Syntax10.Scn.Fnt,, NEW(buf); InitGlobalVars; InstallProtocols8 TMODULE HTTP;   IMPORT Oberon, Input, Texts, TextFrames, Files, Directories, Strings, TCP, Web, In; CONST  TYPE  VAR  PROCEDURE PrependContentKey (id : Web.ContentId; VAR ck : Web.ContentKey);  PROCEDURE EatBlanks (VAR str : ARRAY OF CHAR; which : INTEGER);  PROCEDURE SplitStringAt (sep : ARRAY OF CHAR; VAR split, str : ARRAY OF CHAR);  PROCEDURE IsSameName (s1, s2 : ARRAY OF CHAR) : BOOLEAN;  PROCEDURE Str2Base64 (s : ARRAY OF CHAR; VAR b : ARRAY OF CHAR);  PROCEDURE DateStr (d, t : LONGINT; VAR s : ARRAY OF CHAR);  PROCEDURE ^ HttpOpen (act : Web.UrlStack; txt : Web.Text; elem : Texts.Elem; useCache, auth : BOOLEAN); PROCEDURE NewHeader () : Header;  PROCEDURE ShowStateLine (h : Header);  PROCEDURE ShowHeader (h : Header);  PROCEDURE AnalizeStateLine (h : Header);  PROCEDURE SplitHeader (h : Header);  PROCEDURE AnalizeHeader (t : Task; VAR break : BOOLEAN);  PROCEDURE ReceiveResponse (con : TCP.Connection; VAR r : Files.Rider; avl, exp : LONGINT; head : Header);  PROCEDURE GetResponse;  PROCEDURE HostAdrSet (VAR adr : TCP.IpAdr; url : Web.Url) : BOOLEAN;  PROCEDURE HttpOpen (act : Web.UrlStack; txt : Web.Text; elem : Texts.Elem; useCache, auth : BOOLEAN);  (* procedures for supporting the file-protocol : *) PROCEDURE InitDirFile (f : Files.File; str : ARRAY OF CHAR);  PROCEDURE CloseDirFile (f : Files.File);  PROCEDURE CreateDirFileList (d : Directories.Directory; name : ARRAY OF CHAR; isDir : BOOLEAN; VAR continue : BOOLEAN);  PROCEDURE FileOpen (act : Web.UrlStack; txt : Web.Text; elem : Texts.Elem);  PROCEDURE OpenOberonFile (path : ARRAY OF CHAR);  PROCEDURE (ldr : Loader) Open (act : Web.UrlStack; txt : Web.Text; elem : Texts.Elem; cached : BOOLEAN);  PROCEDURE SetUserAndPw*; (* user-ID:password *)  (* dummy cache-support : *) PROCEDURE NotFound (url : Web.Url; VAR res : INTEGER; VAR d, t : LONGINT);  PROCEDURE NoCache (url : Web.Url; fi : Web.FileInfo; VAR dateTime : ARRAY OF CHAR); END NoCache; PROCEDURE Install*;  PROCEDURE InstallProtocols;  PROCEDURE InitGlobalVars;  BEGIN  END HTTP.