//------------------------------------------------------------------------------
// HTTPServer.cs
// An experimental HTTP Server written in Torque!
// Copyright (c) 2012 Robert MacGregor
//------------------------------------------------------------------------------
// Replicate Code for Servers
$HTTPServer::ServerReplicate = "function *UNIQUESOCKET*::onConnectRequest(%this, %address, %socket)\n" @
"{\n" @
"%this.Parent.connectionCreated(%address, %socket);\n" @
"return true;\n" @
"}\n";
// Replicate Code for Clients
$HTTPServer::ClientReplicate = "function *UNIQUESOCKET*::onLine(%this, %line)\n" @
"{\n" @
"%this.Parent.onLine(%this, %line);\n" @
"return true;\n" @
"}\n" @
"function *UNIQUESOCKET*::onDisconnect(%this) { %this.Parent.connectionDropped(%this); return true; }\n" @
"function *UNIQUESOCKET*::sendPacket(%this,%packet)\n" @
"{\n" @
"%this.send(%packet.statusCode);\n" @
"for (%i = 0; %i < %packet.headerCount; %i++)\n" @
"{\n" @
"%this.send(%packet.headers[%packet.headerName[%i]]);\n" @
"}\n" @
"%this.send(\"\\n\");\n" @
"%this.send(%packet.payLoad);\n" @
"%this.disconnect();\n" @
"}\n";
function HTTPServer::listen(%this, %address, %port, %maxClients)
{
%uniqueNameLength = 6; // Adjust this if need be, but there should never be a reason to
%this.allowMultiConnect = false; // If false, when a client connects twice, its old connection is killed and replaced with a new one.
if (%this.isListening)
return false;
if (%maxClients < 1 || %maxClients == 0)
%maxClients = 8;
%oldAddr = $Host::BindAddress;
%address = strlwr(%address);
if (%address $= "local" || %address $="localhost" ) %address = "127.0.0.1";
else if (%address $= "any") %address = "0.0.0.0";
%charList = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Generate A name for a TCPObject (and make sure it's unique)
%uniqueStringLen = 6; // Adjust this if needed, but there shouldn't be a reason to
%uniqueString = "";
while (true)
{
%uniqueString = generateString(%uniqueNameLength, %charList);
if (!isObject(%uniqueString)) break;
else %uniqueString = "";
}
%evalCode = $HTTPServer::ServerReplicate;
%evalCode = strReplace(%evalCode, "*UNIQUESOCKET*", %uniqueString);
eval(%evalCode);
// Generate a list of unique names that this TCPServer will use (to keep down function def count)
for (%i = 0; %i < %maxClients; %i++)
while (true)
{
%uniqueName = generateString(%uniqueNameLength, %charList);
if (!isObject(%uniqueName))
{
%eval = strReplace($HTTPServer::ClientReplicate, "*UNIQUESOCKET*", %uniqueName);
eval(%eval);
%this.uniqueName[%i] = %uniqueName;
%this.uniqueNameInUse[%uniqueName] = false;
break;
}
}
// Create the Socket and we'll rock n' roll
$Host::BindAddress = %address;
%this.Server = new TCPObject(%uniqueString);
%this.Server.listen(%port);
%this.Server.Parent = %this;
%this.connectionCount = 0;
%this.maximumClients = %maxClients;
$Host::BindAddress = %oldAddr;
%this.isListening = true;
%statusCodes = HTTPServerPrefs.get("StatusCodes",0);
%this.Page[404] = %statusCodes.element("404");
%this.Page[403] = %statusCodes.element("403");
%generic = HTTPServerPrefs.get("Generic",0);
%this.Root = %generic.element("Root");
%this.Variables = Array.create();
%this.MimeTypes = HTTPServerPrefs.get("MIME");
logEcho("Server " @ %uniqueString SPC "is ready on " @ %address @ ":" @ %port);
return true;
}
function HTTPServer::disconnect(%this)
{
}
function HTTPServer::connectionDropped(%this, %socket)
{
if (!IsObject(%socket))
return false;
%socket.disconnect();
if (!%this.allowMultiConnect)
%this.connections[%socket.Address] = "";
else
%this.connections[%socket.Address, %socket.Port] = "";
%this.uniqueNameInUse[%socket.getName()] = false;
%this.connectionCount--;
%this.onClientDisconnect(%socket);
}
function HTTPServer::connectionCreated(%this, %address, %socket)
{
%isReplicate = false;
// Get the Port No. and Address respectively
%address = strReplace(%address, "IP:","");
%portPos = strStr(%address, ":");
%port = getSubStr(%address, %portPos+1, strLen(%address));
%address = getSubStr(%address, 0, %portPos);
// Pick a unique name
%uniqueName = "";
for (%i = 0; %i < %this.maximumClients; %i++)
{
%name = %this.uniqueName[%i];
if (!%this.uniqueNameInUse[%name])
{
%uniqueName = %name;
%this.uniqueNameInUse[%name] = true;
break;
}
}
// If we were unable to find a good unique name
%charList = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
%uniqueStringLen = 6; // Adjust this if needed, but there shouldn't be a reason to
if (%uniqueName $= "")
{
while (true)
{
%uniqueName = generateString(%uniqueStringLen, %charList);
if (!isObject(%uniqueName)) break;
else %uniqueName = "";
}
%eval = strReplace($HTTPServer::ClientReplicate, "*UNIQUESOCKET*", %uniqueName);
eval(%eval);
%this.uniqueName[%this.maximumClients] = %uniqueName;
%this.uniqueNameInUse[%uniqueName] = true;
%this.maximumClients++;
}
// Create The Client Socket
%connection = new TCPObject(%uniqueName,%socket) { class = ConnectionTCP; parent = %this; Address = %address; Port = %port; };
if (!%this.allowMultiConnect)
%this.connections[%address] = %connection;
else
%this.connections[%address, %port] = %connection;
%this.connectionCount++;
%this.onClientConnect(%address, %connection);
logEcho("Received client connection from " @ %address);
%this.schedule(10000,"connectionDropped",%connection);
return true;
}
// Callbacks -- make these do whatever you please!
function HTTPServer::onClientConnect(%this, %address, %socket)
{
echo("Received connection from " @ %address @ ". ID:" @ %socket);
return true;
}
function HTTPServer::onClientRejected(%this, %socket, %reason) // %reason is always zero as of now.
{
return true;
}
function HTTPServer::onClientDisconnect(%this, %socket)
{
logEcho("Received Disconnect (" @ %socket @ ")");
%socket.delete();
return true;
}
function HTTPServer::onLine(%this, %socket, %line)
{
%reqType = getWord(%line, 0);
if (%reqType $= "GET")
{
%req = getWord(%line, 1);
%reqLen = strLen(%req);
%socket.request = getSubStr(%req, 1, %reqLen);
%socket.request = strReplace(%socket.request, "%20", " ");
%socket.request = %this.Root @ %socket.request;
%socket.requestType = "GET";
}
%data = "