Global, thread safe, cookie manager with Indy

My Delphi 2010 application downloads material using multithreading, the downloaded data is sent to a PHP / web application that requires a login, so I need to use a common / common cookie manager (I use Indy10 Revision 4743 ), since TIdCookieManager is not thread safe: (

In addition, on the server side, the session identifier is automatically regenerated every 5 minutes, so I have to synchronize both global and local cookie managers.

My code is as follows:

TUploadThread = class(TThread)
// ...

var
   GlobalCookieManager : TIdCookieManager;

procedure TUploadThread.Upload(FileName : String);
var
   IdHTTP           : TIdHTTP;
   TheSSL           : TIdSSLIOHandlerSocketOpenSSL;
   TheCompressor    : TIdCompressorZLib;
   TheCookieManager : TIdCookieManager;
   AStream          : TIdMultipartFormDataStream;
begin
     ACookieManager := TIdCookieManager.Create(IdHTTP);

     // Automatically sync cookies between local & global Cookie managers
     @TheCookieManager.OnNewCookie := pPointer(Cardinal(pPointer( procedure(ASender : TObject; ACookie : TIdCookie; var VAccept : Boolean)
     begin
          OmniLock.Acquire;
          try
             GlobalCookieManager.CookieCollection.AddCookie(ACookie, TIdHTTP(TIdCookieManager(ASender).Owner).URL{IdHTTP.URL});
          finally
                  OmniLock.Release;
          end;    // try/finally

          VAccept := True;
     end )^ ) + $0C)^;
     // ======================================== //


     IdHTTP         := TIdHTTP.Create(nil);
     with IdHTTP do
     begin
          HTTPOptions     := [hoForceEncodeParams, hoNoParseMetaHTTPEquiv];
          AllowCookies    := True;
          HandleRedirects := True;
          ProtocolVersion := pv1_1;

          IOHandler       := TheSSL;
          Compressor      := TheCompressor;
          CookieManager   := TheCookieManager;
     end;    // with

     OmniLock.Acquire;
     try
        // Load login info/cookies
        TheCookieManager.CookieCollection.AddCookies(GlobalCookieManager.CookieCollection);
     finally
            OmniLock.Release;
     end;    // try/finally

     AStream         := TIdMultipartFormDataStream.Create;

     with Stream.AddFile('file_name', FileName, 'application/octet-stream') do
     begin
          HeaderCharset  := 'utf-8';
          HeaderEncoding := '8';
     end;    // with

     IdHTTP.Post('https://www.domain.com/post.php', AStream);
     AStream.Free;
end;

But that will not work! I get this exception when calling AddCookies ()

MyEXE.exe EAccessViolation " 00000000. 00000000".

assign(), ..

 TheCookieManager.CookieCollection.Assign(GlobalCookieManager.CookieCollection);

, :

 TIdCookieManager.GenerateClientCookies()

- , ?

+2
3

:

, , , AddCookies(), , FRWLock.BeginWrite; TIdCookies.LockCookieList(AAccessType: TIdCookieAccess): TIdCookieList;

Read of address 00000000, . , - , nil.

, . , , , Self, FRWLock nil. , , .

+2

OnNewCookie. :

procedure TUploadThread.NewCookie(ASender: TObject; ACookie : TIdCookie; var VAccept : Boolean);
var
  LCookie: TIdCookie;
begin
  LCookie := TIdCookieClass(ACookie.ClassType).Create;
  LCookie.Assign(ACookie);
  OmniLock.Acquire; 
  try 
    GlobalCookieManager.CookieCollection.AddCookie(LCookie, TIdHTTP(TIdCookieManager(ASender).Owner).URL); 
  finally 
    OmniLock.Release; 
  end;
  VAccept := True;
end;

:

procedure TUploadThread.NewCookie(ASender: TObject; ACookie : TIdCookie; var VAccept : Boolean);
begin
  OmniLock.Acquire; 
  try 
    GlobalCookieManager.CookieCollection.AddServerCookie(ACookie.ServerCookie, TIdHTTP(TIdCookieManager(ASender).Owner).URL); 
  finally 
    OmniLock.Release; 
  end;
  VAccept := True;
end;

:

procedure TUploadThread.Upload(FileName : String); 
var 
  IdHTTP           : TIdHTTP; 
  TheSSL           : TIdSSLIOHandlerSocketOpenSSL; 
  TheCompressor    : TIdCompressorZLib; 
  TheCookieManager : TIdCookieManager; 
  TheStream        : TIdMultipartFormDataStream; 
begin 
  IdHTTP := TIdHTTP.Create(nil); 
  try
    ...
    TheCookieManager := TIdCookieManager.Create(IdHTTP); 
    TheCookieManager.OnNewCookie := NewCookie;

    with IdHTTP do 
    begin 
      HTTPOptions     := [hoForceEncodeParams, hoNoParseMetaHTTPEquiv]; 
      AllowCookies    := True; 
      HandleRedirects := True; 
      ProtocolVersion := pv1_1; 

      IOHandler       := TheSSL; 
      Compressor      := TheCompressor; 
      CookieManager   := TheCookieManager; 
    end;    // with 

    OmniLock.Acquire; 
    try 
      // Load login info/cookies 
      TheCookieManager.CookieCollection.AddCookies(GlobalCookieManager.CookieCollection); 
    finally 
      OmniLock.Release; 
    end;

    TheStream := TIdMultipartFormDataStream.Create; 
    try
      with TheStream.AddFile('file_name', FileName, 'application/octet-stream') do 
      begin 
        HeaderCharset  := 'utf-8'; 
        HeaderEncoding := '8'; 
      end;

      IdHTTP.Post('https://www.domain.com/post.php', TheStream); 
    finally
      TheStream.Free; 
    end;
  finally
    IdHTTP.Free;
  end;
end; 
+5

, , -:

 // Automatically sync cookies between local & global Cookie managers
 @TheCookieManager.OnNewCookie := pPointer(Cardinal(pPointer( procedure(ASender : TObject; ACookie : TIdCookie; var VAccept : Boolean)
 begin
      OmniLock.Acquire;
      try
         GlobalCookieManager.CookieCollection.AddCookie(ACookie, TIdHTTP(TIdCookieManager(ASender).Owner).URL{IdHTTP.URL});
      finally
              OmniLock.Release;
      end;    // try/finally

      VAccept := True;
 end )^ ) + $0C)^;

I'm not sure if there is a magic number $0C, but I'm sure all these throws are there because you had time to get a compiler to accept this. This gave you type errors saying that you cannot assign one thing to another.

Errors of this type exist for some reason! If you hack your way around the type system, things are likely to break. Try turning this anonymous method into a regular method on TUploadThread and assign it that way and see if it works better.

+3
source

All Articles