TFPHTTPServer freezing after Edge browser request lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- Abrax · 2021-02-05 18:30 Dear All, I have a very strange phenomenon on my embedded webserver. It is a console application, which uses a common threaded webserver by the "Pure Hand Coding" way. The debug version is running locally on a Win10x64 system which is connected through a CISCO VPN to a remotely PGSQL database, running on a RPi4 in a corporate intranet. The release version is running on the same RPi as the PGSQL. In this latter case I can access the webserver through the VPN as well. The served html pages are created in severeal ways: from spreadsheet's stream output; static .htlm files from the file system; or assembled by code. The starting page is coming from a GET request, any other pages come from POST requests. After starting the program, calling the http://localhost:8080 (or http://10.xxx.xxx.xxx:8080 in release) in the IE11 or in the Firefox browser, everything works fine. The browsers load the start page, and I can navigate between pages which use the Code: HTML5  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i
request without problem. However, things only work well, until I open the page in an Edge browser. As soon as I open the http://localhost:8080 from an Edge browser, the server doesn't respond to the IE11 and Firefox requests anymore, until the Edge request the same page as the IE or the Firefox. Let's see an example! 1. We have 3 pages. The Firefox can navigate between them 1->2->3->1. 2. We start the Edge with page1. 3. Back to the Firefox we try to navigete to page 2, but it can't, until we choose the page 2 on Edge too. When on the web page I push the button which fires the POST request several times, while the IE11 is waiting locked, I get an "Error reading data from the socket" (code 10054) from the TFPHTTPConnection object on the application console. I have no idea how to overcome this situation. I welcome any suggestion or help. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program webl; {$mode objfpc}{$H+} uses  {$IFDEF UNIX}  //{$IFDEF UseCThreads}  cthreads,  {$ENDIF}//{$ENDIF}  Classes, SysUtils, CustApp//, ...  { you can add units after this }; var  Application: TMyApplication;begin  Application:=TMyApplication.Create(nil);  Application.Title:='WebL';  Application.Run;  Application.Free;end. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit webserver; {$mode delphi} interface uses  Classes, SysUtils,  fphttpserver, HTTPDefs, httpprotocol; type     { THTTPServerThread }     THTTPServerThread = class(TThread)  private    //FServer : TFPHTTPServer;  public    FServer : TFPHTTPServer;    OnRequest1: THTTPServerRequestHandler;    OnRequestError1 : TRequestErrorHandler;    OnAcceptIdle1: TNotifyEvent;    constructor Create(APort : Word; const OnRequest : THTTPServerRequestHandler; const OnRequestError : TRequestErrorHandler; const OnAcceptIdle: TNotifyEvent);    procedure Execute; override;    procedure DoTerminate; override;  end; implementation constructor THTTPServerThread.Create(APort: Word; const OnRequest: THTTPServerRequestHandler; const OnRequestError : TRequestErrorHandler; const OnAcceptIdle: TNotifyEvent);begin  OnRequest1:=OnRequest;  OnRequestError1:=OnRequestError;  OnAcceptIdle1:=OnAcceptIdle;   FServer:=TFPHTTPServer.Create(Nil);  FServer.Port:=APort;  FServer.OnRequest:=OnRequest;  FServer.OnRequestError:=OnRequestError;   inherited Create(False);end; procedure THTTPServerThread.Execute;begin  try    FServer.Active:=True;  finally    FreeAndNil(FServer);  end;end; procedure THTTPServerThread.DoTerminate;begin  inherited DoTerminate;  FServer.Active:=False;end; end.  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit webmodul; {$mode delphi} interface uses  Classes, SysUtils, Math, //StdCtrls,  //Graphics,  fphttpserver, HTTPDefs, httpprotocol, fphttpapp, fpwebfile, webserver, fpmimetypes,  htmlconttype, {...};   TWebModul = class(TObject)  private    FServer1 : THTTPServerThread;  procedure DoHandleRequest (Sender: TObject; var ARequest: TFPHTTPConnectionRequest; var AResponse : TFPHTTPConnectionResponse);    procedure DoHandReqError(Sender : TObject; E : Exception);    procedure DoAcceptIdle (Sender: TObject);  protected    {...}  public    constructor Create(AdatModule: TDataModule1);overload;    destructor Destroy; override;  published  end; implementation { TWebModul } constructor TWebModul.Create(AdatModule: TDataModule1);  //AdatModule is the database related unitbegin  inherited Create;  FServer1 := THTTPServerThread.Create(AdatModule1.WebPort,DoHandleReque st,DoHandReqError,DoAcceptIdle);  //AdatModule1.WebPort:=8080end;{...}  //******************************************************************* **************** procedure TWebModul.DoHandleRequest(Sender: TObject;  var ARequest: TFPHTTPConnectionRequest;  var AResponse: TFPHTTPConnectionResponse); var  file1: TMemoryStream; begin  file1 := TMemoryStream.Create;  {...}  if {ARequest.ContentFields.Values=} true    then      begin        fromspreadsheet(file1);  //from the spreadsheet unit         AResponse.FreeContentStream:= true;        AResponse.ContentStream :=file1;        AResponse.ContentType:=htmlcontenttype('.html');  //:='text/html;charset=utf-8'         AResponse.Code:=200;        AResponse.SendContent;      end;  {...}  file1.Free; end; ---------------------------------------------------------------------- egsuh · 2021-02-06 01:23 I have written webservers only those that are given as templates from Lazarus IDE, and cannot delve into your code. Only from experience a long time ago, I remember that IE acted differently from other browsers, like Forefox or Chrome. IE creates a new instance of webserver when called from the same PC --- at that time, I used ISAPI by Delphi --- while others did not (or vice versa? not remember exactly as this is 10 years ago). I mean, when I opened only one page of IE then it acted as expected but IE acted differently if there were other webbrowsers open and accessing the same web module (.dll file).  I checked this by accessing a global-scope variable (outside class TWebModule). I cannot imagine exact operation for now, but these phenomena may be related with your problem. ---------------------------------------------------------------------- trev · 2021-02-06 04:26 Quote from: egsuh on February 06, 2021, 02:23:39 amI mean, when I opened only one page of IE then it acted as expected but IE acted differently if there were other webbrowsers open and accessing the same web module (.dll file). Two decades ago I used Delphi 5 (and web broker?) to create tax calculators as ISAPI DLLs for an IIS webserver. The webserver generally had 1,500 concurrent accesses during business hours and their was never an issue with the calculators or the HTML pages which they served or produced for results. ---------------------------------------------------------------------- egsuh · 2021-02-06 06:21 QuoteThe webserver generally had 1,500 concurrent accesses during business hours and their was never an issue with the calculators or the HTML pages which they served or produced for results. I do not say there are any problem in ISAPI web server. Mine was not a problem nor error ---- just server's different way of processing requests from IE and other browsers. Firefox and Chrome were treated as if they were coming from the same machine, but IE was as if from different source, even though all of them were running on the same PC, which allso served web server. His web URL is localhost so I reminded that experience. Even though my hypothesis is right, I cannot figure it out right now why he is experiencing such a phenomenon. But somebody may have answers. ---------------------------------------------------------------------- devEric69 · 2021-02-06 11:00 Hello, I don't have the answer to your problem. I use fpWeb as development framework, when it comes to write a CGI application. [off topic] But, I'm currently only using a local LAMP named XAMPP (Apache on Linux) or a local WAMP named UniServer (Apache on Windows; this binary released server is written in Pascal too). For your information, Apache hangs up as often, when it is asked for things that are more complex than normal. The tuning of web requests with Apache-CGI is very frequent.[/off topic] Nevertheless, even with a complete framework specialized in {fast}CGI, when a problem occurs, we obviously need to be able to understand the bug, so we must record the execution context. And for that, off course, we must put in place the minimum, which is amho: - a custom exception handler for your TCGIApplication, i.e. a line like Application.OnShowRequestException:= @customExceptionHandler; . - frame buggy code areas, with defensive calls to try..except..end; . - return some web user informations with EHTTP.Create(...); . - be able to retrieve, track CGI environment variables, from call to call. With such information, it's possible to manage the consistency of an Internet\Intranet user, by notifying him that your application is only compatible with such browsers (at worst), or that he should not change browsers during the progress of your wizard. In short, it remains - first and foremost - an Application just like the others. ---------------------------------------------------------------------- Abrax · 2021-04-15 10:30 I found the source of the strange behavior. I should have set the FServer.Threaded:=true; in the THTTPServerThread.Create coustructor. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i constructor THTTPServerThread.Create(APort: Word; const OnRequest: THTTPServerRequestHandler;  const OnRequestError : TRequestErrorHandler; const OnAcceptIdle: TNotifyEvent; OnDisconnectedByClient : TDisconnectedByClientHandler);begin  OnRequest1:=OnRequest;  OnRequestError1:=OnRequestError;  OnAcceptIdle1:=OnAcceptIdle;  OnDisconnectedByClient1 := OnDisconnectedByClient;   FServer:=TFPHTTPServer.Create(Nil);  FServer.Port:=APort;  FServer.QueueSize:=100;  FServer.OnRequest:=OnRequest;  FServer.OnRequestError:=OnRequestError;    //***************************//  FServer.Threaded:=true;      // <-- this is it  //***************************//   inherited Create(False);end; ---------------------------------------------------------------------- Thaddy · 2021-04-15 10:50 Shouldn't it be:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i begin  inherited Create(False); // this should be first!  OnRequest1:=OnRequest;  OnRequestError1:=OnRequestError;  OnAcceptIdle1:=OnAcceptIdle;  OnDisconnectedByClient1 := OnDisconnectedByClient;   FServer:=TFPHTTPServer.Create(Nil);  FServer.Port:=APort;  FServer.QueueSize:=100;  FServer.OnRequest:=OnRequest;  FServer.OnRequestError:=OnRequestError;   //***************************//  FServer.Threaded:=true;      // <-- this is it  //***************************//end;Reason: the inherited create may otherwise overwrite settings you passed before. Maybe not in this case, but better safe than sorry. The reverse is true for the destructor: inherited destroy should be last if required. ---------------------------------------------------------------------- devEric69 · 2021-04-16 07:11 Well done, Thaddy!