include "osfiles.s7i";
include "process.s7i";
include "listener.s7i";
include "cgi.s7i";
const type: browserConnection is new struct
var listener: inetListener is listener.value;
var file: sock is socket.value;
var process: program is process.value;
var boolean: keepAlive is FALSE;
end struct;
const func string: processHttpRequest (inout browserConnection: browser, inout paramHashType: paramHash) is func
result
var string: filePath is "";
local
var string: line is "";
var string: requestCommand is "";
var string: requestPath is "";
var string: hostName is "";
var integer: spacePos is 0;
var integer: questionMarkPos is 0;
var string: queryParams is "";
var string: postParams is "";
var string: buffer is "";
var string: contentType is "";
var string: contentLengthStri is "";
var integer: contentLength is 0;
begin
browser.keepAlive := FALSE;
line := getln(browser.sock);
while line <> "" do
if startsWith(line, "GET") then
requestCommand := "GET";
requestPath := trim(line[4 ..]);
spacePos := pos(requestPath, ' ');
if spacePos <> 0 then
requestPath := requestPath[.. pred(spacePos)];
end if;
elsif startsWith(line, "POST") then
requestCommand := "POST";
requestPath := trim(line[5 ..]);
spacePos := pos(requestPath, ' ');
if spacePos <> 0 then
requestPath := requestPath[.. pred(spacePos)];
end if;
elsif startsWith(line, "Host") then
hostName := trim(line[succ(pos(line, ":")) ..]);
elsif startsWith(line, "Connection") then
browser.keepAlive := trim(line[succ(pos(line, ":")) ..]) = "keep-alive";
elsif startsWith(line, "Content-Length") then
contentLengthStri := trim(line[succ(pos(line, ":")) ..]);
block
contentLength := integer(contentLengthStri);
exception
catch RANGE_ERROR:
contentLength := -1;
end block;
end if;
line := getln(browser.sock);
end while;
if requestCommand = "GET" then
questionMarkPos := pos(requestPath, '?');
if questionMarkPos <> 0 then
queryParams := requestPath[succ(questionMarkPos) ..];
requestPath := requestPath[.. pred(questionMarkPos)];
end if;
filePath := replace(requestPath, "\\", "/");
paramHash := getCgiParameters(queryParams);
elsif requestCommand = "POST" then
questionMarkPos := pos(requestPath, '?');
if questionMarkPos <> 0 then
queryParams := requestPath[succ(questionMarkPos) ..];
requestPath := requestPath[.. pred(questionMarkPos)];
end if;
if contentLengthStri <> "" then
while contentLength <> 0 do
buffer := gets(browser.sock, contentLength);
contentLength -:= length(buffer);
postParams &:= buffer;
end while;
else
buffer := gets(browser.sock, 10000000);
while buffer <> "" do
postParams &:= buffer;
buffer := gets(browser.sock, 10000000);
end while;
end if;
buffer := "";
filePath := replace(requestPath, "\\", "/");
paramHash := getCgiParameters(postParams);
else
paramHash := paramHashType.value;
end if;
end func;
const func browserConnection: openBrowser is func
result
var browserConnection: browser is browserConnection.value;
local
var integer: port is 1080;
const integer: MAX_PORT is 1100;
var string: filePath is "";
var paramHashType: paramHash is paramHashType.value;
begin
repeat
block
browser.inetListener := openInetListener(port);
exception
catch FILE_ERROR:
incr(port);
end block;
until browser.inetListener <> listener.value or port >= MAX_PORT;
if browser.inetListener <> listener.value then
listen(browser.inetListener, 10);
if fileType("/usr/bin/firefox") <> FILE_ABSENT then
browser.program := startProcess("/usr/bin/firefox", [] ("localhost:" <& port));
elsif fileType("/usr/bin/chromium") <> FILE_ABSENT then
browser.program := startProcess("/usr/bin/chromium", [] ("http://localhost:" <& port));
elsif fileType("/c/Program Files/Mozilla Firefox/firefox.exe") <> FILE_ABSENT then
browser.program := startProcess("/c/Program Files/Mozilla Firefox/firefox.exe", [] ("localhost:" <& port));
elsif fileType("/c/Program Files (x86)/Mozilla Firefox/firefox.exe") <> FILE_ABSENT then
browser.program := startProcess("/c/Program Files (x86)/Mozilla Firefox/firefox.exe", [] ("localhost:" <& port));
elsif fileType("/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe") <> FILE_ABSENT then
browser.program := startProcess("/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe", [] ("http://localhost:" <& port));
elsif fileType("/c/Program Files/Internet Explorer/iexplore.exe") <> FILE_ABSENT then
browser.program := startProcess("/c/Program Files/Internet Explorer/iexplore.exe", [] ("http://localhost:" <& port));
elsif fileType("/c/Program Files (x86)/Internet Explorer/iexplore.exe") <> FILE_ABSENT then
browser.program := startProcess("/c/Program Files (x86)/Internet Explorer/iexplore.exe", [] ("http://localhost:" <& port));
elsif fileType("/Applications/Safari.app/Contents/MacOS/Safari") <> FILE_ABSENT and
fileType(commandPath("open")) <> FILE_ABSENT then
browser.program := startProcess(commandPath("open"), [] ("-a", "safari", "http://localhost:" <& port));
end if;
browser.sock := accept(browser.inetListener);
filePath := processHttpRequest(browser, paramHash);
end if;
end func;
const proc: close (inout browserConnection: browser) is func
begin
block
close(browser.sock);
exception
catch FILE_ERROR:
noop;
end block;
end func;
const proc: acceptNewSock (inout browserConnection: browser) is func
local
var boolean: hasNext is FALSE;
begin
repeat
browser.sock := accept(browser.inetListener);
hasNext := hasNext(browser.sock);
if not hasNext then
close(browser.sock);
end if;
until hasNext;
end func;
const proc: sendClientError (inout file: sock, in integer: statuscode,
in string: message, in string: explanation) is func
local
var string: response is "";
var string: htmlMessage is "";
begin
htmlMessage := "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n\
\<html><head>\n\
\<title>" <& statuscode <& " " <& message <& "</title>\n\
\</head><body>\n\
\<h1>" <& message <& "</h1>\n\
\<p>" <& explanation <& "</p>\n\
\<hr>\n\
\<address>Comanche</address>\n\
\</body></html>\n";
response := "HTTP/1.1 " <& statuscode <& " " <& message <& "\r\n\
\Server: Comanche\r\n\
\Transfer-Encoding: identity\r\n\
\Content-Length: " <& length(htmlMessage) <& "\r\n\
\Content-Type: text/html\r\n\
\\r\n";
response &:= htmlMessage;
write(sock, response);
end func;
const proc: display (inout browserConnection: browser, inout webPage: currWebPage) is func
local
var string: filePath is "";
var paramHashType: paramHash is paramHashType.value;
begin
send(currWebPage, browser.sock);
if not browser.keepAlive then
close(browser);
end if;
if currWebPage.isForm then
repeat
if not browser.keepAlive then
acceptNewSock(browser);
end if;
filePath := processHttpRequest(browser, paramHash);
if filePath <> "/" then
block
sendClientError(browser.sock, 404, "Not Found",
"The requested URL " <& filePath <&
" was not found on this server.");
exception
catch FILE_ERROR:
noop;
end block;
if not browser.keepAlive then
close(browser);
end if;
end if;
until filePath = "/";
update(currWebPage, paramHash);
end if;
end func;