See the Almonaster page for more information on getting the sample page source up and running
Alájar is an portable HTTP server that implements a subset of the HTTP protocol and provides a foundation upon which powerful web-based applications can be built.
The objective of Alájar is to make it easy for developers to develop fast interactive web content. Currently, the most commonly used method of developing web applications that generate dynamically generated HTML content in response to form submissions from web clients is CGI scripting. CGI scripts can be implemented in a variety of languages, and the results are both powerful and flexible.
However, CGI suffers from an important drawback: a lack of scalability. Each form submission sent by the client requires the server to launch a new process to handle it, usually either a Perl interpreter or a compiled C application. Most CGI scripts execute and terminate rapidly, so the cost of the OS creating and destroying each new process is quite high when compared to the actual work that the processes perform. Furthermore, if many different spawned processes are executing at the same time, the cost of context switches between them will be a significant bottleneck on the server's throughput. For a minimal number of clients the server will perform well, but when the server is under stress from many different simultaneous hits, the overhead of CGI scripting can rapidly become ridiculously high.
Different solutions to this problem have been proposed by different web server vendors. One of the most popular is Microsoft's Active Server Pages (ASP), which use server-side scripting in two scripting languages (VBScript and JavaScript) to provide interactive content. The problem with this solution is that interpretation of these scripting languages is slow enough that it is usually faster to call out to external COM components than to perform any intensive calculations in the scripts themselves. This both complicates the development process and makes execution sluggish because of the cost of creating new COM objects and switching context from interpreted to compiled code. However, the advantage of scripting languages over CGI is that no new processes need to be created, since the interpretation is handled in-process.
The solution proposed by the development of Alájar is to combine the best of both worlds: under the Alájar programming model, compiled web application code is executed in-process via dynamically linked libraries that implement the standard Alájar interface. In this way the application can control exactly what the web server will send back to the client, and the full power and flexibility of CGI can be obtained without suffering its costs.
Alájar can operate both as a standard web server and as a framework for developers to write custom web applications. Through the creation of simple text configuration files, web administrators can create virtual domains (hereafter referred to as page sources) under which incoming requests from clients can either be treated as simple HTTP requests or be passed along to application code in order to generate a custom response. Using this scheme, Alájar allows interactive HTML pages to be built on the fly in a fast and scalable manner.
It should be noted that both Java applets and JavaScript / Dynamic HTML can be used with Alájar, simply because they are client side technologies that are merely delivered by the server through text or binary files. Legacy CGI code is not excluded from the Alájar development model either; future versions of Alájar will include a page source named "cgi-bin" that will launch external applications in exactly the same way that other web servers do.
The services offered by Alájar include the following:
Minimal system requirements for a basic Alájar server would be:
A configuration file called Alajar.conf must exist in the same directory as the Alájar executable. This file must contain the following parameters (the values given are examples). Order does not matter, spaces before and after the '='sign are forbidden and no slashes at the end of directory names should be used:
Port=80
FileCache=1
ApproxNumFilesInFileCache=2000
InitNumThreads=5
MaxNumThreads=30
DefaultThreadPriority=Normal
CounterPath=Counters
LogPath=Logs
PageSourcePath=PageSources
ConfigPath=Config
ReportPath=Reports
StatisticsPath=Statistics
The directories specified must exist on the computer running Alájar. Path names can be absolute or relative to the path of the Alájar executable. It is advisable to use UNIX-style slashes ("/") instead of DOS style backslashes ("\") as directory separators.
The configuration files in the ConfigPath directory must contain the following parameters (the values given are examples):
Name=Default
401File=Errors/401.html
403File=Errors/403.html
404File=Errors/404.html
500File=Errors/500.html
501File=Errors/501.html
Override401=0
Override403=0
Override404=0
AllowDirectoryBrowsing=1
BasePath=www_root
DefaultFile=index.html
UsePageSourceLibrary=1
PageSourceLibrary=Admin.dll
PageSourceClsId=8B631302-8CFA-11d3-A240-0050047FE2E2
OverrideGet=0
OverridePost=0
UseBasicAuthentication=0
UseDefaultFile=1
UseLogging=1
UseCommonLogFormat=1
UseSSI=1
DenyIPAddressAccess=
OnlyAllowIPAddressAccess=127.0.0.1@192.168.0.*
DenyUserAgentAccess=Mozilla/2.0 (Compatible; AOL-IWENG 3.0; Win16)
OnlyAllowUserAgentAccess=
These parameters are used to configure the default page source, which is the one used when no other page source matches the request URI. Each page source must have a file with identical contents in the ConfigPath directory (*.conf) in order to be registered with Alájar upon startup.
Daily log files are written on a per-page source basis into the server's log directory.
Daily statistics files are written on a global basis into the server's log directory.
The heart of Alájar is the HttpServer object, which contains the core functionality of the web server. An HttpServer object contains a FileCache object that provides it with file I/O, a ThreadPool object to which a main socket delegates client requests, and a hash table of PageSource objects.
The FileCache provides a standard GetFile() / Release() interface for its clients. The backend is a hash table containing CachedFile objects corresponding to the files in the cache. The FileCache relies on the OS for memory mapped I/O and page replacement policies.
The HttpServer object uses two special classes to communicate with the page sources: HttpRequest and HttpResponse. An HttpRequest object is assigned to each new client connection; it contains logic that, given an open socket, decodes the HTTP headers from the client and makes the information available to the page source. HttpResponse objects contain the logic that handles the server responses: they allow page sources to specify the nature of the response that the client should receive. Responses can be HTTP error codes, URL redirects, files or data buffers with an associated mime type. Response objects also contain lists of cookies to be associated with the client or deleted.
Execution of Alájar proceeds as follows: an HttpServer object is created and started, after which the main server thread is spawned and the main function blocks until a termination signal is received.
When the server starts it configures itself according to the Alajar.conf configuration file and the page source configuration files found in the Config directory, after which it opens a socket and listens on the specified port number. When an incoming connection is received, the task of responding is passed on to the ThreadPool, which stores the task in a FIFO queue. Eventually a worker thread will pick up the task, and obtain an HttpRequest object that will decode the HTTP request and hand it off to an HttpResponse object. If the request falls under the aegis of a particular page source (as determined by the first directory name referenced in the request's URI), then that page source is called and its response is passed along to the client. Otherwise, the request is handled like a normal HTTP request by the default page source, the response being either a file or an error message.
When data is POST'ed by a client, the server passes the data on to the appropriate page source for processing. If a file is submitted via a file requester form, then the file is saved as a temporary file and the name is passed to the page source. If no page source can be identified, then the server discards the submitted data and returns a standard error message.
It is important to observe that if a page source is configured to be free-threaded, there are no guarantees that the same thread will consistently call into the same page source for all requests. Consequently, no data should be stored by the page source in thread local storage and no thread specific operations should be performed. However, if the page source is configured to be single threaded, it is guaranteed that it will be called into with the same thread.
Download the sdk to obtain the headers and libs needed to develop for Alájar.
Alájar provides a plug-in architecture for web applications in which custom code can be invoked when the client requests a resource from a particular URL. Alájar plug-ins are called page sources, and are implemented in page source DLL's. Multiple page sources can be implemented in one DLL. Each page source is a C++ object that implements the IPageSource interface, as defined in Alajar.h. The sample Admin and Almonaster page sources that ship with Alájar provide good examples of development methodology.
Every page source DLL must provide the following export function:
extern "C" int CreateInstance (
const Uuid& uuidClsid,
const Uuid& uuidIid,
void** ppObject
);
(Note: The Uuid type referenced below is a DCE universally unique identifier, defined in Osal's IObject.h)
The uuidClsid argument is the Uuid specified in the page source's .conf file. If a library implements several page sources, this function will be called once for each different page source.
The uuidIid argument is the Uuid of the interface that Alajar expects the class referenced by uuidClsid to implement. At present, this argument will always be IID_IPageSource and will refer to the IPageSource interface as defined in Alajar.h. The actual definition of IID_IPagesource is in Alajar.lib.
If the page source recognized the class id and interface id, it should send back an instance of the interface in ppObject and return OK. Otherwise, it should return ERROR_FAILURE.
The IPageSource interface contains the following methods:
int OnInitialize (
IHttpServer* pHttpServer,
IPageSource* pPageSource,
IFileCache* pFileCache,
IReport* pReport,
ILog* pLog,
IConfigFile* pConfig
);
int OnFinalize(
);
int OnGet (
IHttpRequest* pHttpRequest,
IHttpResponse* pHttpResponse
);
int OnPost (
IHttpRequest* pHttpRequest,
IHttpResponse* pHttpResponse
);
int OnBasicAuthenticate
(
const char* pszLogin,
const char* pszPassword
);
int On401Error (
IHttpRequest* pHttpRequest,
IHttpResponse* pHttpResponse
);
int On403Error (
IHttpRequest* pHttpRequest,
IHttpResponse* pHttpResponse
);
int On404Error (
IHttpRequest* pHttpRequest,
IHttpResponse* pHttpResponse
);
If the page source has requested to override IP address or user agent access
denied errors, then it must implement the matching exports:
int OnIPAddressDeniedAccess (
IHttpRequest* pHttpRequest,
IHttpResponse* pHttpResponse
);
int OnUserAgentDeniedAccess (
IHttpRequest* pHttpRequest,
IHttpResponse* pHttpResponse
);
OnGet and OnPost are called when the corresponding events are generated by a client request. All available information about the request is provided through the I HttpRequest interface, and all information about what the page source's response should be is provided to the server via the IHttpResponse interface. The value returned should OK if all is well and ERROR_FAILURE if an internal error occurred (the report can be used to provide more information concerning the error).
OnInitialize is called when the web server is starting up, while OnFinalize is called when the web server is shutting down. These two functions allow page sources (which may contain complex data structures and file I/O schemes) to execute construction and destruction code when appropriate. The interface pointers provided in OnInitialize are should be copied and stored by the page source, and there is no need to AddRef and Release them. The same rules as with OnGet apply in terms of return values.
On401Error, On401Error and On404Error are called when the corresponding errors are generated by a client request, even when returned by the OnGet or OnPost methods. The same rules as with OnGet apply in terms of return values.
OnIPAddressDeniedAccess and OnUserAgentDeniedAccess are called when the page source's access control rules block access to a client. The same rules as with OnGet apply in terms of return values.
OnBasicAuthenticate is called when basic authentication is requested by a page source and a client requires authentication in order to proceed. If the password is accepted, OK is returned, and if not ERROR_FAILURE.
The IHttpRequest interface is used by page sources to obtain information about the client's request. The following methods can be used:
The IHttpResponse interface is used by page sources to define the response that will be sent back to the client. The following methods can are used:
Other status codes will be treated as internal errors.
Aside from these constraints, page sources can more or less do whatever they want. However, some care must be taken not to corrupt memory or generate unhandled exceptions, since the page source code runs in the same process as the Alájar executable and other page sources.
The IHttpServer interface is used by page sources to obtain information about the server that they are operating under. The following methods can be used:
[To be completed]
The IHttpForm interface is used to obtain information about a given form submission:
To facilitate the development of web applications with Alájar, a text preprocessor called Asfpp (Alájar Scripting Format PreProcessor) has been developed. Asfpp takes as input a text file in scripting format and produces C++ code as output. To illustrate this procedure, a file with the following content:
<% #include <stdio.h>
%>
<html>
<% for (int i = 0; i < 100; i ++) { %>
<p><% Write (i);
} %>
</html>
... would produce the following C++ code:
#include "Alajar.h"
#include <stdio.h>
#define Write pHttpResponse->WriteText
int RenderTest (IHttpRequest*
pHttpRequest,
IHttpResponse* pHttpResponse, Context* pContext) {
pHttpResponse->WriteData ("\n\n<html>\n\n");
for (int i = 0; i < 100; i ++) {
pHttpResponse->WriteData
("\n<p>");
pHttpResponse->WriteData(i);
}
pHttpResponse->WriteData ("\n\n</html>");
}
The result, when compiled and executed by a page source, would be an HTML file containing the following text:
<html>
<p>0
<p>1
<p>2
...
<p>99
</html>
The ASF format was designed to resemble other popular scripting language formats and facilitate the conversion of code from these to Alájar. The <% and %> markers are used to encapsulate C++ code, while the rest of the text is the HTML to be sent to the IHttpResponse object.
Asfpp is included in the standard Alájar distribution.
Alájar includes a page source called admin that provides basic administrative functionality. access to /admin is password protected via Alájar's built-in support for basic authentication. Through the admin page source, administrators can: