The easiest way to implement an HTTP server is to drop a TIdHTTPServer component (part of the Indy package, included with Delphi) onto a form or data module, create a handler for its OnCommandGet event (which strikes when a client makes a request to the server) and set its Active property to True. Once the application runs, it will start listening to requests on the port specified in the TIdHTTPServer component (default is 8000).
The code in our web quiz server application logs all incoming requests and the corresponding responses, and uses an object of type TWebQuizServerManager (more about this later) to handle the actual logic. This is implemented using the following OnCommandGet handler:
procedure TWebQuizServerMainForm.HTTPServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); var Path: String; PeerIP: String; Params: TStrings; PeerPort: Integer; begin Path := ARequestInfo.Document; Params := ARequestInfo.Params; PeerIP := AContext.Connection.Socket.Binding.PeerIP; PeerPort := AContext.Connection.Socket.Binding.PeerPort; Log(ARequestInfo.RawHTTPCommand, PeerIP, PeerPort); AResponseInfo.ContentText := Manager.ProcessRequest(Path, Params); Log(AResponseInfo.ContentText, PeerIP, PeerPort); end;
Note that the parameters for the OnCommandGet event differ a bit between different versions of the Indy package. I have tested the project with Delphi 7 and Delphi XE. In the downloadable code, two different versions are handled using compiler directive VER150 to separate Delphi 7 code from newer versions.
If you are new to web development, the first thing to realize about a web application like this is that it is a combination of JavaScript code running in the browser (executed on the user’s computer) and code running in our server (the Delphi application we are about to create).
We need to start by establishing a “communication protocol” between the client and the server. By this, I dont’t mean HTTP, but a protocol on a higher level. An example could be that if the client sends “/” in its request, then the server should respond with the initial code (HTML, JavaScript and CSS) needed. If the client sends “/answer” in its request, then the server should interpret the supplied parameters, determine if the supplied answer is the correct one and return a message indicating if the answer was correct or not. The full protocol of our quiz server is specified in the following picture:
We can see, from the above code, that all requests are passed on to the game manager. It is the game manager’s job to handle the requests and produce suitable responses. In more detail, the manager does the following:
- Determine if the request is a valid one (‘/’, ‘/quiz_select’, ‘/quiz_start’, ‘/quiz_answer’ or ‘/quiz_next_question’).
- Use the game classes described in the previous articles to help fulfill the request (get a list of games, get a new question, get a result etc.)
- Create a suitable response (typically HTML, but in a few cases also JavaScript or CSS). For most of the HTML responses, a temple .HTML file is loaded and its contents altered. In some simple cases, however, the HTML code is generated in the application, without loading anything from a template file.
Once the game manager has produced a response, we are back in the OnCommandGet handler, where we return it to the client.
To keep track of a game’s progress, we include a game’s session ID in all HTML code returned to the client while a game is in progress. The session ID is stored in an invisible <div> (see “quiz_question.html”). When the client sends an answer, it supplies the session ID.
Note that we use a XMLHttpRequest object to be able to make calls to the server, and present the result of the calls in a <div> component, without having to reload the whole page each time.
Structure of our web server
In this picture, we see the game manager, as well as the quiz and session classes.
The quiz classes
The shaded rectangle on the right shows our quiz-specific classes. TQuizManager and TQuizGame are described in detail in the first article. TPreloadedIniFileQuizManager is a quiz manager that loads quiz data from an ini file when created, and then keeps that data in memory. In essence it is a combination of the THardcodedQuizManager and TIniFileQuizManager classes from the first article.
The session classes
The other shaded rectangle shows our session-specific classes. TSession, TSessionContainer and TSession-Manager are all described in the second article. TWebQuizSession inherits from TSession, and introduces a variable of type TQuizGame.
You can download the code here.