FastCGI (again...)

Just starting out? Need help? Post your questions and find answers here.
Motu
Enthusiast
Enthusiast
Posts: 160
Joined: Tue Oct 19, 2004 12:24 pm

FastCGI (again...)

Post by Motu »

Okay, now I got it to work - also it was some bad piece of pain. So, for the few of you how want to write there own FastCGI Apps for windows server 2008, here you go:

Code: Select all

EnableExplicit

#MaxThread    = 1

Global *FCGI_SetExitStatus
Global *FCGX_Accept_r
Global *FCGX_FClose
Global *FCGX_FPrintF
Global *FCGX_Finish_r
Global *FCGX_GetParam
Global *FCGX_Init
Global *FCGX_InitRequest
Global *FCGX_SetExitStatus

Global FCGI_Lib.i
Global Mutex.l

Global ErrFile.s

Structure S_FCGX_Request
 requestId.l
 role.l
 *in
 *out
 *err
 *envp

 ipcFd.l
 isBeginProcessed.l
 keepConnection.l
 appStatus.l
 nWriters.l
 flags.l
 listen_sock.l
 BetterToMuchThenToLess.l[1000]
EndStructure


; Will display an error Message
Procedure FCGI_ErrMsg(String.s)
 Define File.i
 File = CreateFile(#PB_Any,ErrFile)
 If File <> 0
  WriteStringN(File,String)
  CloseFile(File)
 Else
  ; You'll never know what happend...
 EndIf
 End
EndProcedure
; End Err_Msg(String.s)

; Will make sure all function are avaible
Procedure FCGI_GetFunctionSave(Lib.i,Name.s)
 Define Function.i
 
 Function = GetFunction(Lib, Name)
 If Function = 0
  FCGI_ErrMsg("Cannot find pointer to function " + Name)
 Else
  ProcedureReturn Function
 EndIf
EndProcedure
; End FCGI_GetFunctionSave(Lib.i,Name.s)

; Will do some Init stuff
Procedure FCGI_Init()
 ; Init logging and error display
 ErrFile = "err.txt"
 DeleteFile(ErrFile)
 
 FCGI_Lib = OpenLibrary(#PB_Any, "libfcgi.dll")

 If FCGI_Lib = 0
  FCGI_ErrMsg("Cannot open libfcgi.dll")
 EndIf
 *FCGI_SetExitStatus   = FCGI_GetFunctionSave(FCGI_Lib, "FCGI_SetExitStatus")
 *FCGX_Accept_r        = FCGI_GetFunctionSave(FCGI_Lib, "FCGX_Accept_r")
 *FCGX_FClose          = FCGI_GetFunctionSave(FCGI_Lib, "FCGX_FClose")
 *FCGX_FPrintF         = FCGI_GetFunctionSave(FCGI_Lib, "FCGX_FPrintF")
 *FCGX_Finish_r        = FCGI_GetFunctionSave(FCGI_Lib, "FCGX_Finish_r")
 *FCGX_GetParam        = FCGI_GetFunctionSave(FCGI_Lib, "FCGX_GetParam")
 *FCGX_Init            = FCGI_GetFunctionSave(FCGI_Lib, "FCGX_Init")
 *FCGX_InitRequest     = FCGI_GetFunctionSave(FCGI_Lib, "FCGX_InitRequest")
 *FCGX_SetExitStatus   = FCGI_GetFunctionSave(FCGI_Lib, "FCGX_SetExitStatus")

EndProcedure
; End FCGI_Init()

; Removes program from memory
Procedure FCGI_ReInit()
 CloseLibrary(0)
 End
EndProcedure
; End FCGI_ReInit()

; Print a String with Line Break
Procedure FCGI_PrintN(*out,String.s)
 Define LineBreak.s = Chr(13) + Chr(10)
 If Len(String) > 0
  CallCFunctionFast(*FCGX_FPrintF, *out, @String)
 EndIf
 CallCFunctionFast(*FCGX_FPrintF, *out, @LineBreak)
EndProcedure
; End FCGI_PrintN(String.s)

; The Main Loop
Procedure FCGI_Main(Index.l)
 Define Result.i, String.s, Count.i, Name.s
 Define *in, *out, *err, *envp, *Param
 Define QUERY_STRING.s
 Define PATH_TRANSLATED.s
 Define LOCAL_ADDR.s
 Define ScriptTyp.i, ScriptValuei.i
 Define ScriptName.s, ScriptValue.s
 Define *ThreadRequest.S_FCGX_Request = AllocateMemory(SizeOf(S_FCGX_Request))
 
 CallCFunctionFast(*FCGX_InitRequest,*ThreadRequest, 0, 0)
 Repeat
  LockMutex(Mutex)
   Result = CallCFunctionFast(*FCGX_Accept_r,*ThreadRequest)
  UnlockMutex(Mutex)
  
  Delay(5000)
  If Result => 0
   Name.s = "QUERY_STRING"
   *Param = CallCFunctionFast(*FCGX_GetParam,@Name,*ThreadRequest\envp)
   QUERY_STRING = PeekS(*Param)
   Name.s = "PATH_TRANSLATED"
   *Param = CallCFunctionFast(*FCGX_GetParam,@Name,*ThreadRequest\envp)
   PATH_TRANSLATED = PeekS(*Param)
   Name.s = "LOCAL_ADDR"
   *Param = CallCFunctionFast(*FCGX_GetParam,@Name,*ThreadRequest\envp)
   LOCAL_ADDR = PeekS(*Param)

   FCGI_PrintN(*ThreadRequest\out,"Content-type: text/html")
   FCGI_PrintN(*ThreadRequest\out,"")
   Count + 1
   
   String.s = "Hello Thread Nr:" + Str(Index) ; Test Output
   FCGI_PrintN(*ThreadRequest\out,String)
   
   CallCFunctionFast(*FCGX_Finish_r,*ThreadRequest)
  EndIf
 Until Result < 0
 ;FCGI_Log("FCGI_Accept < 0 - End of Process: " + Str(Result))
 Debug "Done"
 CallCFunctionFast(*FCGX_SetExitStatus, 0)
 
EndProcedure
; End Main()

FCGI_Init()

Define n.i

Mutex = CreateMutex()
CallCFunctionFast(*FCGX_Init)
For n = 2 To #MaxThread
 CreateThread(@FCGI_Main(),n)
Next

FCGI_Main(1)

FCGI_ReInit()
Anyway, there is a huge problem and I just do get if (if anyone could help - I'd be very thankfull). When using multithreaded version (more than one thread - you can try by simply setting #MaxThread > 1) it's just not working - CallCFunctionFast(*FCGX_Finish_r,*ThreadRequest) crashes the application when a second thread is waiting. So please, if anyone knows want's wrong, say it :-)

BTW:

here is the original code from http://www.fastcgi.com in c that was base for this:

Code: Select all

/*
 * threaded.c -- A simple multi-threaded FastCGI application.
 */

#ifndef lint
static const char rcsid[] = "$Id: threaded.c,v 1.9 2001/11/20 03:23:21 robs Exp $";
#endif /* not lint */

#include "fcgi_config.h"

#include <pthread.h>
#include <sys/types.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "fcgiapp.h"


#define THREAD_COUNT 20

static int counts[THREAD_COUNT];

static void *doit(void *a)
{
    int rc, i, thread_id = (int)a;
    pid_t pid = getpid();
    FCGX_Request request;
    char *server_name;

    FCGX_InitRequest(&request, 0, 0);

    for (;;)
    {
        static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
        static pthread_mutex_t counts_mutex = PTHREAD_MUTEX_INITIALIZER;

        /* Some platforms require accept() serialization, some don't.. */
        pthread_mutex_lock(&accept_mutex);
        rc = FCGX_Accept_r(&request);
        pthread_mutex_unlock(&accept_mutex);

        if (rc < 0)
            break;

        server_name = FCGX_GetParam("SERVER_NAME", request.envp);

        FCGX_FPrintF(request.out,
            "Content-type: text/html\r\n"
            "\r\n"
            "<title>FastCGI Hello! (multi-threaded C, fcgiapp library)</title>"
            "<h1>FastCGI Hello! (multi-threaded C, fcgiapp library)</h1>"
            "Thread %d, Process %ld<p>"
            "Request counts for %d threads running on host <i>%s</i><p><code>",
            thread_id, pid, THREAD_COUNT, server_name ? server_name : "?");

        sleep(2);

        pthread_mutex_lock(&counts_mutex);
        ++counts[thread_id];
        for (i = 0; i < THREAD_COUNT; i++)
            FCGX_FPrintF(request.out, "%5d " , counts[i]);
        pthread_mutex_unlock(&counts_mutex);

        FCGX_Finish_r(&request);
    }

    return NULL;
}

int main(void)
{
    int i;
    pthread_t id[THREAD_COUNT];

    FCGX_Init();

    for (i = 1; i < THREAD_COUNT; i++)
        pthread_create(&id[i], NULL, doit, (void*)i);

    doit(0);

    return 0;
}

Fred
Administrator
Administrator
Posts: 18154
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: FastCGI (again...)

Post by Fred »

Nice work. You could use "Import/EndImport" to reduce the code size and remove all the wrapping/check about the functions.
Motu
Enthusiast
Enthusiast
Posts: 160
Joined: Tue Oct 19, 2004 12:24 pm

Re: FastCGI (again...)

Post by Motu »

Thanks :)
But if anybody could help me with the multithread problem I would be even more happy.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: FastCGI (again...)

Post by Trond »

Smee again! 8)

Code: Select all

Global Mutex.l
Better use .i or it may break on 64-bit.

Are you compiling with threadsafe? Because you should.
Motu
Enthusiast
Enthusiast
Posts: 160
Joined: Tue Oct 19, 2004 12:24 pm

Re: FastCGI (again...)

Post by Motu »

Yes, I compiled as thread safty...
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: FastCGI (again...)

Post by Trond »

Works fine here with 10 threads. I get a reply from a random thread. (Btw. I forgot to enable threadsafe.)
Hello Thread Nr:8
Motu
Enthusiast
Enthusiast
Posts: 160
Joined: Tue Oct 19, 2004 12:24 pm

Re: FastCGI (again...)

Post by Motu »

mmh - maybe I have some old dll or something? Where did you get the dll from? What PB version do you use? What version of the IIS do you use?
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: FastCGI (again...)

Post by Trond »

1st problem: it suddenly crashed while I did nothing. So everything is obviously not ok. Recompiling with threadsafe seems to have solved it so far.
Motu wrote:Where did you get the dll from?
I compiled it from this source: http://fastcgi.com/dist/fcgi-2.4.0.tar.gz
What PB version do you use?
4.41 RC1.
What version of the IIS do you use?
None. Maybe this explains it.... I use Abyss web server.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: FastCGI (again...)

Post by Trond »

No there it crashed again.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: FastCGI (again...)

Post by Trond »

Got it (I hope). FCGX_SetExitStatus() crashes. You call it with the wrong number of parameters, you would probably have noticed this had you followed Fred's suggestion of using ImportC. Anyhow it crashes even if I pass it the correct number of parameters, maybe I pass some invalid value. Just commenting out the call makes everything work.

Code: Select all

EnableExplicit

#MaxThread    = 2

ImportC "libfcgi.lib"
  FCGI_SetExitStatus(Status.i)
  FCGX_Accept_r(*Request)
  FCGX_FClose(*Stream)
  FCGX_FPrintF(*Stream, *Format)
  FCGX_Finish_r(*Request)
  FCGX_GetParam(*Name, *EnvArray)
  FCGX_Init()
  FCGX_InitRequest(*Request, Sock.i, Flags.i)
  FCGX_SetExitStatus(Status.i, *Stream)
EndImport

Global Mutex.i
Global File.i
Global ErrFile.s
Global LogMutex.i
Global Quit.i
Global Dim Threads(#MaxThread)

Structure S_FCGX_Request
  requestId.i
  role.i
  *in
  *out
  *err
  *penvp
  
  ipcFd.i
  isBeginProcessed.i
  keepConnection.i
  appStatus.i
  nWriters.i
  flags.i
  listen_sock.i
  ; BetterToMuchThenToLess.l[1000]
EndStructure

Procedure FCGI_ErrMsg(String.s)
  LockMutex(LogMutex)
  If File <> 0
    FileSeek(File, Lof(File))
    WriteStringN(File,String)
    FlushFileBuffers(File)
  Else
    MessageRequester("", String)
  EndIf
  UnlockMutex(LogMutex)
EndProcedure

Procedure FCGI_Init()
  ; Init logging and error display
  ErrFile = "c:\err.txt"
  DeleteFile(ErrFile)
  File = OpenFile(#PB_Any, ErrFile)
  LogMutex = CreateMutex()
EndProcedure

; Print a String with Line Break
Procedure FCGI_PrintN(*out, String.s)
  String + #CRLF$
  FCGX_FPrintF(*out, @String)
EndProcedure

; The Main Loop
Procedure FCGI_Main(Index.l)
  Protected Result.i
  Protected String.s
  Define *Param
  Define QUERY_STRING.s
  Define PATH_TRANSLATED.s
  Define LOCAL_ADDR.s
  Define ThreadRequest.S_FCGX_Request
  
  FCGX_InitRequest(@ThreadRequest, 0, 0)
  Repeat
    LockMutex(Mutex)
     Result = FCGX_Accept_r(@ThreadRequest)
    UnlockMutex(Mutex)
    
    If Result => 0
      *Param = FCGX_GetParam(@"QUERY_STRING", ThreadRequest\penvp)
      QUERY_STRING = PeekS(*Param)
      *Param = FCGX_GetParam(@"PATH_TRANSLATED", ThreadRequest\penvp)
      PATH_TRANSLATED = PeekS(*Param)
      *Param = FCGX_GetParam(@"LOCAL_ADDR", ThreadRequest\penvp)
      LOCAL_ADDR = PeekS(*Param)
      
      FCGI_PrintN(ThreadRequest\out,"Content-type: text/html")
      FCGI_PrintN(ThreadRequest\out,"")
      
      ;Test Output
      String.s = "Hello Thread Nr:" + Str(Index)
      String + "<br>QUERY_STRING: " + QUERY_STRING
      FCGI_PrintN(ThreadRequest\out,String)
       
      If QUERY_STRING = "fcgi_stop"
        Quit = 1
      EndIf
       
      FCGX_Finish_r(ThreadRequest)
    EndIf
  Until Result < 0 Or Quit
  
  ;FCGX_SetExitStatus(0, ThreadRequest\out)
  
  FCGI_ErrMsg("FCGI_Accept < 0 - End of Thread " + Str(Index) + ": " + Str(Result) + ", " + QUERY_STRING)

EndProcedure

; Compile with OnError lines support
Procedure ErrorHandler()
  FCGI_ErrMsg("Error at line " + Str(ErrorLine()))
  MessageRequester("Error", "Line: " + Str(ErrorLine()))
EndProcedure
OnErrorCall(@ErrorHandler())

FCGI_Init()

Define n.i

Mutex = CreateMutex()
FCGX_Init()
For n = 2 To #MaxThread
  Threads(n) = CreateThread(@FCGI_Main(),n)
Next

FCGI_Main(1)
For n = 2 To #MaxThread
  WaitThread(Threads(n))
Next

Motu
Enthusiast
Enthusiast
Posts: 160
Joined: Tue Oct 19, 2004 12:24 pm

Re: FastCGI (again...)

Post by Motu »

Hi Trond,
thank you very much :-)

I would like to try it, but where did you get the libfcgi.lib from? I cannot find it anyway - could you mail me your version of the .lib and the .dll ? I send you my mail adresse as a privat message.

edit: I see you have a link there, but I have some problem to make the sourcecode version work with visual studio 2008 - what c compiler did you use? And - it would be cool if you could send me your version so I can check if it has to do with my .dll
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: FastCGI (again...)

Post by Trond »

visual studio 2008 - what c compiler did you use?
Visual C++ 2008 express edition.
When you compile to dll, the lib file is generated in the same folder as the dll.

To compile it, open the Visual Studio 2008 command prompt from the start menu, go to the folder where you unpacked the archive and type "nmake -f Makefile.nt". This compiles, libfcgi, unfortunately it uses a dynamically linked runtime library, so it won't run without msvcrt.dll or something. But the point is, after running this command, the project files suddenly start working. :)

1. Do the above
2. Open Win32\libfcgi.dsp
3. Open Project -> libfcgi properties, select "configuration properties" in the treeview and then "all configurations" in the combobox above.
4. Set a few options:
C/C++ -> Optimizations -> Omit frame pointers = Yes
C/C++ -> Code Generations -> Runtime library = /MT
Click "Ok".
5. Select "Release" (instead of "Debug") in the main toolbar.
6. Build -> Build solution
7. Click "Save" if asked.

Now look in C:\usrc\fcgi-2.4.0\libfcgi\Release for a nice surprise.
Motu
Enthusiast
Enthusiast
Posts: 160
Joined: Tue Oct 19, 2004 12:24 pm

Re: FastCGI (again...)

Post by Motu »

Hi Trond,
thank you very much for your help - i have compiled my own version of the dll and lib now - but, anyway, it's not working in multithreaded mode as well. If I use the importC function or not doesn't matter. BTW: FCGX_SetExitStatus should in normal cases never be reached, as the process runs permanent
Any more suggestions? Or must I die?
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: FastCGI (again...)

Post by Trond »

BTW: FCGX_SetExitStatus should in normal cases never be reached, as the process runs permanent
Abyss web server seems to unload the process after a few minutes of inactivity.
it's not working in multithreaded mode as well.
What do you mean by "not working"? Is it still FCGX_Finish_r() which is crashing? Did you remember threadsafe?
Motu
Enthusiast
Enthusiast
Posts: 160
Joined: Tue Oct 19, 2004 12:24 pm

Re: FastCGI (again...)

Post by Motu »

Yes, I remebered thread safty and yes FCGX_Finish_r crashes. Could you send me your .exe und dll and lib? Maybe it's a problem with the IIS 7
Post Reply