Site Map Contact Us Home
E-mail Newsletter
Subscribe to get informed about
Clever Components news.

Your Name:
Your Email:
 
SUBSCRIBE
 
Previous Newsletters
 




Products Articles Downloads Order Support
Customer Portal      

Multithreaded HTTP Server in Delphi

HTTP Server in Delphi

Abstract

This article represents the fully functional multithreaded multi connection HTTP server that works asynchronously in a thread pool, accepts the GET, POST, PUT and any other HTTP requests, and sends the corresponding user-defined responses.

The TclHttpServer component is built based on the Clever Internet Suite library and uses the fast and stable classes: TclTcpServer, TclUserConnection, and TclThreadPool.

 

Implementation

The Clever Internet Suite library provides a special TclTcpServer basic component, which can be used as an ancestor class for implementing a new server component. We need to override the following two virtual methods in order to create a new user connection and handle the request data:

// [Delphi]
TclHttpServer = class(TclTcpServer)
protected
   function CreateDefaultConnection: TclUserConnection; override;
   procedure DoReadConnection(AConnection: TclUserConnection; AData: TStream); override;
end;

// [Delphi]
function TclHttpServer.CreateDefaultConnection: TclUserConnection;
begin
   Result := TclHttpUserConnection.Create(HttpVersion, CharSet);
end;

// [Delphi]
procedure TclHttpServer.DoReadConnection(AConnection: TclUserConnection; AData: TStream);
begin
   inherited DoReadConnection(AConnection, AData);

   //handle the request data here
end;

 

The TclHttpUserConnection object holds the request parameters, such as the request URI, request method, header, cookies, and the request body. If you need to specify the response headers, response cookies, or the response version, you can do that by assiging the corresponding TclHttpUserConnection property.

// [Delphi]
TclHttpUserConnection = class(TclUserConnection)
public
   function AcceptRequestData(AData: TStream): Boolean;

   property RequestVersion: TclHttpVersion read FRequestVersion;
   property RequestMethod: string read FRequestMethod;
   property RequestUri: string read FRequestUri;
   property RequestHeader: TclHttpRequestHeader read FRequestHeader;
   property RequestCookies: TStrings read FRequestCookies;

   property ResponseVersion: TclHttpVersion read FResponseVersion write FResponseVersion;
   property ResponseHeader: TclHttpResponseHeader read FResponseHeader;
   property ResponseCookies: TStrings read FResponseCookies;

   property RequestBody: TStream read FRequestBody write SetRequestBody;
end;

 

When a request is received, the DoReadConnection method is called. We need to read the request header, parse it, and accept the rest of the request data. The DoReadConnection method handles the server errors as well. If there is HTTP protocol error, the server returns the error status response. Otherwise, the OnServerError event fires. This event belongs to the TclTcpServer component. If the connection is still available, the server attempts to send the “Internal error” status to the client with the code 500.

The Content-Length header field should be extracted and analyzed. The TclHttpUserConnection class implements this functionality.

// [Delphi]
procedure TclHttpServer.DoReadConnection(AConnection: TclUserConnection; AData: TStream);
var
   conn: TclHttpUserConnection;
begin
   inherited DoReadConnection(AConnection, AData);

   conn := TclHttpUserConnection(AConnection);
   try
      if conn.AcceptRequestData(ARequest) then
      begin
         // raises the OnReceiveRequest event
         DoReceiveRequest(conn, conn.RequestMethod, conn.RequestUri,
            conn.RequestHeader, conn.RequestBody);
      end;
   except
      on E: EclHttpServerError do
      begin
         SendResponseAndClose(conn, E.ErrorCode, E.Message, E.Message);
      end;
   end;
end;

 

Each request handling should be ended by sending appropriate response.

We need to send the HTTP response header and the response body, if latter exists. A set of overloaded SendResponse methods does all the work. There is also a set of SendResponseAndClose methods. The only difference is that the SendResponseAndClose methods close the connection when the response sending completed. The cause of this behavior is an asynchronous nature of the server. Data is transferred asynchronously after exiting the SendResponse and SendResponseAndClose methods.

// [Delphi]
procedure TclHttpServer.SendResponse(AConnection: TclHttpUserConnection;
   AStatusCode: Integer; const AStatusText: string; ABody: TStream);
begin
   try
      SendResponseHeader(AConnection, AStatusCode, AStatusText, ABody);

      AConnection.WriteData(ABody);
   finally
      AConnection.Clear();
   end;
end;

 

How to use the server

The usage of the TclHttpServer component is simple. After downloading and installing the Clever Internet Suite library, simply download the HttpServer project, compile and install it.

There is only one important component event should be handled: OnReceiveRequest. This event is used for obtaining the request data and sending the server response. The other component events can be used for, e.g. logging purposes.

Note: the component events are not synchronized with the main application thread. For accessing the VCL objects, such as TEdit and TMemo, a thread synchronization code should be used.

// [Delphi]
procedure TForm1.clHttpServer1ReceiveRequest(Sender: TObject;
   AConnection: TclHttpUserConnection; const AMethod, AUri: string;
   AHeader: TclHttpRequestHeader; ABody: TStream);
begin
   PutLogMessage('Request: ' + AMethod + ' ' + AUri + ' Length: ' + IntToStr(ABody.Size));

   AConnection.ResponseHeader.ContentType := 'text/html';

   clHttpServer1.SendResponse(AConnection, 200, 'OK',
      '<html><body>Your requested the ' + AUri + ' resource.</body></html>');
end;

// [Delphi]
procedure TForm1.clHttpServer1AcceptConnection(Sender: TObject;
   AConnection: TclUserConnection; var Handled: Boolean);
begin
   PutLogMessage('Accept Connection. Host: ' + AConnection.PeerIP);
end;

// [Delphi]
procedure TForm1.PutLogMessage(const ALogMessage: string);
begin
   FSynchronizer.Enter();//an instance of TCriticalSection
   try
      memLog.Lines.Add(ALogMessage);
   finally
      FSynchronizer.Leave();
   end;
end;

 

Downloads

Download HttpServer Component and Demo Program on GitHub

Clever Internet Suite Download

Learn more about Clever Internet Suite Library

Ask a Question

 

Sergey Shirokov
Clever Components team
www.clevercomponents.com

    Copyright © 2000-2024