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.
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.)
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.