I use this blog to help me memories useful programming techniques that I discover from any source in the world and combined with my creativity. So that if I need it in the future I can read it here.
Web server is a program or sub program that provide data to client program not limited to web browser using HTTP or HTTPS (HTTP over SSL) protocol. HTTP is a simple, fast and robust protocol for client-server application. The HTTP protocol works by having the client send a request to the server, then the server processes the request and sends back a response to the client.
Example of HTTP Client's Request:
GET / HTTP/1.1 Host: www.example.com
The first line of a HTTP request consist of a command, a url and the protocol version. Followed by header lines and terminated by empty line. Each header line consist of header name and value separated by colon and one space. After the header data is the content data that contain n bytes where n is the value of Content-Length header.
Example of HTTP Server's Response:
HTTP/1.1 200 OK Date: Mon, 23 May 2005 22:38:34 GMT Content-Type: text/html; charset=UTF-8 Content-Length: 155 Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux) ETag: "3f80f-1b6-3e1cb03b" Accept-Ranges: bytes Connection: close <html> <head> <title>An Example Page</title> </head> <body> <p>Hello World, this is a very simple HTML document.</p> </body> </html>
The first line of a HTTP response consist of protocol version and result code followed by error message. Followed by header lines and terminated by empty line. Each header line consist of header name and value separated by colon and one space. After the header data is the content data that contain n bytes where n is the value of Content-Length header.
Free Pascal, Delphi and Lazarus share the same programming language the Object Pascal with little differentiation. So many Delphi component can be used in Lazarus and vice versa because it only requires minor changes in the source code to support both programming. If you don't want to use visual component you can use operating system API like winsock (Windows Socket) for Windows or unisock (Unix Socket) for Linux. I will use synapse for Free Pascal library in this example.
To help parsing input data I create TStringSplitter :
TStringSplitter = class(TObject)
private
FLine: String;
FCol: Integer;
procedure SetCol(AValue: Integer);
procedure SetLine(AValue: String);
public
constructor Create;
function Fetch(const ATerminator: String=''): String;
property Line: String read FLine write SetLine;
property Col: Integer read FCol write SetCol;
end;
function PosAfter(const SubText, Text: String; StartAt: Integer): Integer;
var
I, L1, L2: Integer;
begin
Result := -1;
L1 := Length(SubText);
L2 := Length(Text);
if ((L1 > L2) or (StartAt < 1)) then exit;
for I := StartAt to L2-L1+1 do
if (CompareMem(@SubText[1], @Text[I], L1)) then
begin
Result := I;
exit;
end;
end;
begin
FCol := AValue;
end;
begin
FLine := AValue;
FCol := 1;
end;
begin
FLine := '';
FCol := 1;
end;
var
I, J: Integer;
begin
Result := '';
I := FCol;
if (I > Length(FLine)) then exit;
if (ATerminator <> '') then
begin
J := PosAfter(ATerminator, FLine, I);
if (J > 0) then
begin
Result := Copy(FLine, I, J-I);
FCol := J + Length(ATerminator);
exit;
end;
end;
Result := Copy(FLine, FCol, MaxInt);
FCol := Length(FLine) + 1;
end;
When the server is started it initialize the socket to listen to the specified port. Then it repeatly check for incoming connection and start HTTP handler worker thread until the server is terminated.
procedure TDaemon1.WorkServerListener(Thread: TWorkerThread);
var
sock: TTCPBlockSocket;
h: TSocket;
begin
try
InitPatchMan;
sock := TTCPBlockSocket.Create;
try
sock.Bind('0.0.0.0',IntToStr(Port));
if (sock.LastError <> 0) then
raise ENetworkError.Create('Cannot bind to port '+IntToStr(Port));
sock.Listen;
if (sock.LastError <> 0) then
raise ENetworkError.Create('Cannot listen to port '+IntToStr(Port));
while (not Thread.Terminated) do
begin
if (sock.CanRead(0)) then
begin
h := sock.Accept;
{$HINTS OFF}
if (h <> INVALID_SOCKET) then
WorkerThreads.AddNew(@WorkRequestHandler,Pointer(h));
{$HINTS ON}
end
else
Sleep(1);
end;
sock.CloseSocket;
finally
sock.Free;
end;
except
on E: Exception do
// DebugLog('HTTP Server terminated by error '+E.ClassName+': '+
// E.Message);
end;
end;
The HTTP handler worker thread read HTTP request from incoming connection and send back HTTP Respond.
var
sock: TTCPBlockSocket;
S1: TStringSplitter;
s, cmd, url, http_ver, nm, vl, error_msg, respond_text, respond_data: AnsiString;
I, result_code: Integer;
close_socket, can_read: Boolean;
headers, respond_headers: TStrings;
begin
result_code := 500;
error_msg := 'Internal Server Error';
close_socket := True;
sock := TTCPBlockSocket.Create;
S1 := TStringSplitter.Create;
headers := TStringList.Create;
respond_headers := TStringList.Create;
try
{$HINTS OFF}
sock.Socket := TSocket(Thread.Param);
{$HINTS ON}
repeat
can_read := sock.CanRead(0);
if (sock.LastError <> 0) then exit;
if (can_read) then
begin
S1.Line := sock.RecvString(ReadTimeout);
if (sock.LastError <> 0) then exit;
cmd := S1.Fetch(' ');
url := S1.Fetch(' ');
http_ver := S1.Fetch;
if ((cmd = 'GET') or (cmd = 'POST')) then
begin
close_socket := (http_ver <> 'HTTP/0.9') and (http_ver <> 'HTTP/1.0');
S1.Line := sock.RecvString(ReadTimeout);
if (sock.LastError <> 0) then exit;
while (s <> '') do
begin
nm := S1.Fetch(': ');
vl := S1.Fetch;
headers.Values[nm] := vl;
end;
//
// .. do some processing here
//
respond_data :=
'<head><title>Welcome</title></head><body><h1>Welcom to my Web Server</h1><p>You are in '+HttpEncode(url)+'</body>';
respond_headers.Values['Content-Type'] := 'html/text';
respond_headers.Values['Content-Length'] := IntToStr(Length(respond_data));
result_code := 200;
error_msg := 'OK';
end
else begin
result_code := 400;
error_msg := 'Bad Request';
end;
respond_text :=
http_ver + ' ' + IntToStr(result_code) + ' ' + error_msg+#13#10;
for I := 0 to respond_headers.Count do
respond_text := respond_text + respond_headers.Names[I]+': '+
respond_headers.ValueFromIndex[I];
respond_text := respond_text+#13#10+respond_data;
sock.SendBuffer(@respond_text[1], Length(respond_text));
headers.Clear;
respond_headers.Clear;
respond_text := '';
end
else
Sleep(1);
until (Thread.Terminated or close_socket);
finally
respond_headers.Free;
headers.Free;
S1.Free;
sock.Free;
end;
end;
You can send and receive data using easy to parse data like JSON, XML, BSON or binary data for data service, it is very flexible.
That's all for this article and I hope it's useful.

Tidak ada komentar:
Posting Komentar