Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2011-11

fcl-web TFPHttpServer component problem

11 messages · 4 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Graeme Geldenhuys

Hi,

I was testing the newish HTTP Server component located in fcl-web/src/fphttpserver.pp

I simply created a form with Start/Stop button. The problem is, that once I call .Active := True, it never returns from that call, so my test program (and thus any program using this server component) is stuck in limbo land...

Is this a known problem? And more importantly, is there a work-around? I'm using FPC 2.5.1 (the latest revision in the 2.6.0 fixes branch).

procedure TMainForm.ButtonClicked(Sender: TObject);
begin
  if Button1.Text = 'Start' then
  begin
    writeln('before start');
    FHTTPServer.Active := True;
    writeln('after start');
    Button1.Text := 'Stop';
  end
  else
  begin
    writeln('before stop');
    FHTTPServer.Active := False;
    writeln('after stop');
    Button1.Text := 'Start';
  end;
end;

Note:
Once the HTTP Server is running, it does correctly handle and respond to HTTP requests, so the worker thread of the http server is working. It is just that the program implementing the HTTP Server component is frozen, and its process has to be killed to terminate the http server.

Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://fpgui.sourceforge.net
Leonardo M. Ramé
to Graeme Geldenhuys
Show quoted text 51 lines
________________________________ From: Graeme Geldenhuys To: FPC-Pascal users discussions Sent: Monday, November 28, 2011 7:42 AM Subject: [fpc-pascal] fcl-web TFPHttpServer component problem Hi, I was testing the newish HTTP Server component located in fcl-web/src/fphttpserver.pp I simply created a form with Start/Stop button. The problem is, that once I call .Active := True, it never returns from that call, so my test program (and thus any program using this server component) is stuck in limbo land... Is this a known problem? And more importantly, is there a work-around? I'm using FPC 2.5.1 (the latest revision in the 2.6.0 fixes branch). procedure TMainForm.ButtonClicked(Sender: TObject); begin   if Button1.Text = 'Start' then   begin     writeln('before start');     FHTTPServer.Active := True;     writeln('after start');     Button1.Text := 'Stop';   end   else   begin     writeln('before stop');     FHTTPServer.Active := False;     writeln('after stop');     Button1.Text := 'Start';   end; end; Note: Once the HTTP Server is running, it does correctly handle and respond to HTTP requests, so the worker thread of the http server is working. It is just that the program implementing the HTTP Server component is frozen, and its process has to be killed to terminate the http server. -- Regards,   - Graeme -

I think that class was meant to be used as the main program loop. Didn't tested, but what happens if you use it inside a TThread?.  

-- 
Leonardo M. Ramé
http://leonardorame.blogspot.com

Graeme Geldenhuys
to Graeme Geldenhuys: I was testing the newish HTTP Server component located in fcl-web/src/fphttpserver.pp I…
Graeme Geldenhuys wrote 5 lines
I simply created a form with Start/Stop button. The problem is, that once I call .Active := True, it never returns from that call, so my test program (and thus any program using this server component) is stuck in limbo land...

I forgot to mention a few things. Here is the console output after I clicked the Start button. Note the second writeln() statement is never reached.

--------------------------------
[httpserver]$ ./testserver
before start

--------------------------------

I've test this under Ubuntu Linux (64-bit) and Windows 2000 (32-bit). Both give the exact same behaviour.

Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://fpgui.sourceforge.net
Graeme Geldenhuys
to Leonardo M. Ramé: I think that class was meant to be used as the main program loop. Didn't tested, but what…
Leonardo M. Ramé wrote
I think that class was meant to be used as the main program loop.

If so, that's a pretty dumb design. Also the HTTP server is implemented as a component (TComponent descendant), so I gather the thoughts was that some day they would like it to sit on the Lazarus component palette, where it will be dropped onto some form or data module - thus rendering that application frozen too.

Didn't tested, but what happens if you use it inside a TThread?.

I can't see how that would work either. For example, you can never check for the Terminated boolean property (in the customary while loop) inside the thread's Execute method, because as before, the call to Active never returns. So you always need to kill the process - no clean exit ever.

Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://fpgui.sourceforge.net
Unknown author
to Graeme Geldenhuys
Graeme Geldenhuys wrote 5 lines
On 28/11/2011, Leonardo M. Ramé wrote: > > I think that class was meant to be used as the main program loop. If so, that's a pretty dumb design.

Ah. And why is that so ?

Show quoted text 5 lines
Also the HTTP server is implemented as a component (TComponent descendant), so I gather the thoughts was that some day they would like it to sit on the Lazarus component palette, where it will be dropped onto some form or data module - thus rendering that application frozen too.

It was never meant as such.

It is meant as the "main program loop", as Leonardo surmised. The component is meant to be simple.

And using a TComponent does not automatically mean that it will sit on the component palette. I use TComponent because I like the automated memory management it offers; e.g. it also means the TDatamodules it creates for servicing requests can be owned by the server component itself.

If you want still to have a "main program", you should use a thread and create the component in the thread. Which is exactly what happens in a service application, the intended environment for the component.

Michael.

Flávio Etrusco
to Unknown author
Show quoted text 22 lines
> Also the HTTP server is > implemented as a component (TComponent descendant), so I gather the > thoughts was that some day they would like it to sit on the Lazarus > component palette, where it will be dropped onto some form or data > module - thus rendering that application frozen too. It was never meant as such. It is meant as the "main program loop", as Leonardo surmised. The component is meant to be simple. And using a TComponent does not automatically mean that it will sit on the component palette. I use TComponent because I like the automated memory management it offers; e.g. it also means the TDatamodules it creates for servicing requests can be owned by the server component itself. If you want still to have a "main program", you should use a thread and create the component in the thread. Which is exactly what happens in a service application, the intended environment for the component. Michael.

I have to agree this is a very odd interface. IMHO setting a property is expected to return quickly; a call that's meant to be used as "program loop" should be a explicit method and named accordingly... (Listen/Run/Execute/ActivateAndWait/etc)

-Flávio

Graeme Geldenhuys
to Unknown author: Ah. And why is that so ? It was never meant as such. It is meant as the "main program…
> > If so, that's a pretty dumb design. Ah. And why is that so ?

Now it's much less useful.

How are you supposed to "correctly" terminate the HTTP Server once Active = True is called?

If you want still to have a "main program", you should use a thread and create the component in the thread. Which is exactly what happens in a service application, the intended environment for the component.

I fail to see how this will resolve the problem, but that could be contributed to my lack of experience with service-style projects. None the less, I'll adapt my very simple test project and create the server instance inside a thread, and see how it goes. Like I said, I still can't see how you can cleanly terminate the HTTP server, because it will never give the thread a chance to look at the value of TThread.Terminated?

eg: (the following code is just from memory, not actually test)

procedure TMyThread.Execute;
begin
   while not Terminated do
   begin
     FHTTPServer.Active := True;       // We are stuck here forever
  end;
end;

or

procedure TMyThread.Execute;
begin
   FHTTPServer.Active := True;       // We are stuck here forever
   while not Terminated do
   begin
      // do something, but we will never get here
   end;
   FHTTPServer.Active := False;  // we will never get here either.
end;
Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://fpgui.sourceforge.net
Unknown author
to Graeme Geldenhuys
Graeme Geldenhuys wrote 10 lines
On 28/11/2011, wrote: >> >> If so, that's a pretty dumb design. > > Ah. And why is that so ? Now it's much less useful. How are you supposed to "correctly" terminate the HTTP Server once Active = True is called?

1. From a request.
2. From the main thread if the component is set to active in a thread.

Show quoted text 11 lines
> If you want still to have a "main program", you should use a thread and > create the component in the thread. Which is exactly what happens in a > service application, the intended environment for the component. I fail to see how this will resolve the problem, but that could be contributed to my lack of experience with service-style projects. None the less, I'll adapt my very simple test project and create the server instance inside a thread, and see how it goes. Like I said, I still can't see how you can cleanly terminate the HTTP server, because it will never give the thread a chance to look at the value of TThread.Terminated?
The mistake you make is that you create a loop in the thread;
The thread does not need a loop. It just needs to set Active to true:
Procedure TMyServerThread.Execute;
begin
   MyServer.Active:=True;
end;

As soon as a request or the main thread sets active to false, the statement will return and the thread will terminate.

To terminate the server, you should not set the thread to terminated, but set

MyServerThread.MyServer.Active:=False;

or implement

Procedure TMyServerThread.StopServer;
begin
   MyServer.Active:=False;
end;

and call

MyServerThread.StopServer;

Since this will happen from another thread or from a request handler, you can do this without problem.

Michael.

Graeme Geldenhuys
to Unknown author
Show quoted text 9 lines
The mistake you make is that you create a loop in the thread; The thread does not need a loop. It just needs to set Active to true: Procedure TMyServerThread.Execute; begin MyServer.Active:=True; end;

OK thanks, I got it working now but it is not ideal yet.

It is still problematic if you try starting & stopping the HTTP Server because if you stop it, TThread.Execute completes, thus you can't restart it again, you need to create a new thread instance. I guess not the end of the world for my simple example, but might be for other more complex apps. But then, that was probably not the design goal of this component.

Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://fpgui.sourceforge.net
Graeme Geldenhuys
to Flávio Etrusco: I have to agree this is a very odd interface. IMHO setting a property is expected to…
Flávio Etrusco wrote 5 lines
I have to agree this is a very odd interface. IMHO setting a property is expected to return quickly; a call that's meant to be used as "program loop" should be a explicit method and named accordingly... (Listen/Run/Execute/ActivateAndWait/etc)
Definitely!  Currently  .Active := True   is a blocking call, but
.Active := False is not. And as you said, there is no hint that
calling Active := true starts it's own program loop.
Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://fpgui.sourceforge.net
Unknown author
to Graeme Geldenhuys: OK thanks, I got it working now but it is not ideal yet. It is still problematic if you…
Graeme Geldenhuys wrote 19 lines
On 28/11/2011, wrote: > > The mistake you make is that you create a loop in the thread; > The thread does not need a loop. It just needs to set Active to true: > > Procedure TMyServerThread.Execute; > > begin > MyServer.Active:=True; > end; OK thanks, I got it working now but it is not ideal yet. It is still problematic if you try starting & stopping the HTTP Server because if you stop it, TThread.Execute completes, thus you can't restart it again, you need to create a new thread instance. I guess not the end of the world for my simple example, but might be for other more complex apps. But then, that was probably not the design goal of this component.

I am open for suggestions, but the possible solutions are IMHO not so simple:
- Either a thread must be created automatically to actually run the server when Active:=True
- Or the server must be made non-blocking and then you need to run an event loop.

Either way is a significant architectural change.

Michael.