Architecture

The EPL HTTP Server is a multi-threaded HTTP/HTTPS/WebSocket server supporting virtual hosting with multiple independent sites.

Key Files

FilePurpose
superserver.eplEntry point - loads sites.json, initializes modules
http_srv.eplTServer class - thread pool management
onaccept.eplTOnAccept - HTTP parsing state machine
action.eplTAction - request dispatcher
THTTP_Base.eplModule base class
THTTP_Module.eplExtended base with template support
TTemplOperator.eplTemplate processing engine
websocket.eplWebSocket frame parsing

Request Flow

Client Request
    │
TServer::OnAccept() [threaded, one per connection]
    │
TOnAccept [HTTP parsing state machine, steps 1-99]
    │
ProcessHttpData() [matches virtual host by Host header]
    │
TAction [creates request context, GET/POST params]
    │
ProcessUri() [hash-based handler lookup]
    │
Module Handler (THTTP_Base subclass)
    │
SendResponse()

Configuration

Path Aliases (epl_cfg.epl)

AddPathItem("Projects", "/Work/Apps/v0002_11_08_2022/Projects");
AddPathItem("HttpSrv", "/Work/Apps/v0002_11_08_2022/HttpSrv");
AddPathItem("Studio", "/Work/Apps/v0002_11_08_2022/Projects/Studio");

Used in includes:

include_file "$Studio/ElastLib/main_inc.epl";
include_file "$HttpSrv/THTTP_Module.epl";

sites.json Structure

{
  "HttpIps": ["0.0.0.0:80"],
  "HttpsIps": ["0.0.0.0:443"],
  "Sites": [{
    "Uri": "example.com",
    "RedirUri": "https://example.com",
    "Dir": "$CUR_SCR_DIR/../MySite",
    "Module": "$CUR_SCR_DIR/../MySite/THTTP_MySite.epl",
    "Http": 1,
    "Https": 1,
    "WSockName": "wss://example.com",
    "LandPage": "/index.html",
    "RootDir": "$CUR_SCR_DIR/../MySite/content",
    "DbPath": "$CUR_SCR_DIR/../MySite/main.orn",
    "DbClasses": "$CUR_SCR_DIR/../MySite/Classes",
    "DbLog": "$CUR_SCR_DIR/../MySite/log"
  }]
}

SSL Certificates

Place in CertPath directory:

  • cert.pem - CA certificate
  • chain.pem - Certificate chain
  • fullchain.pem - Full certificate chain
  • privkey.pem - Private key

Module Development

Required Include Order

#define _STRINGOP_LIGHT
class TSem : public V_Semaphore{}
class TSem2 : public V_Semaphore{}
class TPack : public V_PackVariant{}
TSem @Sem;
include_file "$Projects/Studio/Sys/TStringOP.epl";
include_file "$Projects/Studio/ElastLib/TJson.epl";
include_file "$Projects/Studio/ElastLib/TProtocol.h";
include_file "$HttpSrv/THTTP_Module.epl";

Module Pattern

class THTTP_mysite : public THTTP_Module {
    int TemplId;
    int SymbolId;
}

THTTP_mysite @instance;
variant vCurModule = instance;           // REQUIRED: server reads this
vCurModule->sGuid = "unique-guid";       // REQUIRED: unique identifier

virtual int THTTP_mysite::Prepare() {
    LoadJson();                          // Load act.json handlers
    TemplOP->PrepareTemplate(TemplId, 2, "page.html");
    SymbolId = TemplOP->GetSymbol("CONTENT");
    return 1;
}

int THTTP_mysite::Handler(TAction ptr Action) {
    variant LocArray;
    apush(LocArray, SymbolId, "CONTENT", "value");
    TemplOP->ProcessTemplate(TemplId, Action, LocArray, Action->OutText);
    return 1;
}

TAction Properties

PropertyDescription
Action->GET["param"]Query string parameters
Action->POST["param"]POST body parameters (or JSON)
Action->UriRequest path
Action->HostTHost virtual host config
Action->OutTextSet response content
Action->isSocket1 if WebSocket request
Action->PAccept->SendFile(path)Send file download
Action->PAccept->SendError(msg, code)Send error response
Action->PAccept->Redirect(url)HTTP redirect

Request Routing (act.json)

{
  "Actions": [{
    "Uri": "login",
    "Function": "OnLogin",
    "Flags": 1,
    "Parms": [{
      "Name": "email",
      "Type": 1,
      "Verify": 3,
      "MinLim": 5,
      "MaxLim": 100
    }]
  }]
}

Parameter Types

0=int, 1=string, 2=float, 3=object, 4=array

Verify Types

0=Int, 1=Login, 2=Password, 3=Email, 4=ItemName, 5=ItemText, 6=Float, 7=LongNote, 8=FileType, 14=Array, 15=Guid, 16=None

Flags

1=FLAG_ALLOWED_WITH_NO_AUTH, 2=FLAG_FILE_DOWNLOAD, 7=FLAG_ALLOWED_SIMPLE_ACTION, 8=FLAG_WBS_ACTION

Template Engine

Template Syntax

  • --VARIABLE-- - Variable replacement
  • **FunctionName** - Callback function
  • ++template++ - Include sub-template

Example Template

<html>
<head><title>--TITLE--</title></head>
<body>
    ++header++
    <main>
        --CONTENT--
        **RenderItems**
    </main>
    ++footer++
</body>
</html>

Callback Signature

void THTTP_mysite::RenderItems(TAction ptr Action, int ptr Index, string ptr Output) {
    Output = "<ul>";
    for(int i=0; i<count(Items); i++){
        Output .= "<li>" . Items[i] . "</li>";
    }
    Output .= "</ul>";
}

WebSocket

Module Virtual Methods

// Called when WebSocket connection initializes
virtual int THTTP_mysite::WS_OnInit(int ptr hSock, TSocketData ptr SocketData);

// Called for binary WebSocket frames (opcode 0x02)
virtual int THTTP_mysite::WS_OnBinaryData(int ptr hSock, BYTE *Buf, int ptr PLen, TSocketData ptr SocketData);

// Called for JSON text frames
virtual int THTTP_mysite::WS_OnJsonData(int ptr hSock, TAction ptr Action, TSocketData ptr SocketData);

TSocketData

class TSocketData {
    int         hSock;      // Socket handle
    TServer     PServer;    // Server reference
    variant     vObj;       // TOnAccept instance
    variant     UserData;   // Module-specific state
}

Sending Messages

// From TOnAccept (use PAccept)
Action->PAccept->SendWbsMessage(text);              // Text frame
Action->PAccept->SendWbsMessageLL(Buf, Len, 1, 0);  // Binary frame

// From TServer (use PServer)
PServer->WbsSend(hSock, text);                      // Text to specific socket
PServer->WbsSendBin(hSock, Buf, Len);               // Binary to socket

WebSocket Opcodes

OpcodeMeaning
0x00Continuation frame
0x01Text data
0x02Binary data
0x08Connection close
0x09Ping
0x0APong

Static File Routing

URL PrefixSource Directory
/files/{RootDir}/files/
/pages/{RootDir}/pages/
/sb/{RootDir}/sb/
/js/{RootDir}/static/js/
/css/{RootDir}/static/css/
/static/{RootDir}/static/