Глобал, потокобезопасен, менеджер куки с Indy

Мое приложение Delphi 2010 загружает данные, используя многопоточность, загруженные данные помещаются в PHP / веб-приложение, которое требует входа в систему, поэтому мне нужно использовать менеджер общих / глобальных файлов cookie (я используюIndy10 Revision 4743), так как TIdCookieManager не является потокобезопасным :(

Кроме того, на стороне сервера, идентификатор сессии автоматически генерируется каждые 5 минут, поэтому яmust сохранить как глобальный & amp; локальные менеджеры печенья в синхронизации.

Мой код выглядит так:

<code>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;
</code>

Но это не работает! Я получаю это исключение при вызове AddCookies ()

Project MyEXE.exe raised exception class EAccessViolation with message 'Access violation at address 00000000. Read of address 00000000'.

Я также пытался использовать assign (), т.е.

<code> TheCookieManager.CookieCollection.Assign(GlobalCookieManager.CookieCollection);
</code>

Но я все еще получаю то же исключение, обычно здесь:

<code> TIdCookieManager.GenerateClientCookies()
</code>

Кто-нибудь знает, как это исправить?

 Remy Lebeau04 мая 2012 г., 01:16
Где ты создаешь и освобождаешьGlobalCookieManager объект? Поскольку он используется глобально, вы должны делать это в @ блоinitialization а такжеfinalization блоки.
 Mason Wheeler03 мая 2012 г., 23:33
Что ты делаешь с заданием OnNewCookie? Когда я вижу несколько слоев приведения указателя, обернутый вокруг анонимного метода, заканчивающийся чем-то вродеend )^ ) + $0C)^; Я немного нервничаю.
 TheDude04 мая 2012 г., 00:05
Спасибо, ребята, я перешел на обычный метод, но я все еще получаю исключения в AddCookies (), последнее произошло в строке, которая читаетFRWLock.BeginWrite; в этой процедуреTIdCookies.LockCookieList(AAccessType: TIdCookieAccess): TIdCookieList;
 TheDude03 мая 2012 г., 23:43
Я согласен, что это не лучший код, но, как я писал в коде, OnNewCookie поддерживает синхронизацию как локальных, так и глобальных менеджеров Cookie (и, насколько я могу судить, проблема не в событии OnNewCookie)
 Remy Lebeau03 мая 2012 г., 23:43
Я согласен с @MasonWheeler.OnNewCookieобытие @ ожидает нестатический метод экземпляра объекта, а не анонимную процедуру.TIdCookieManager собирается пройти скрытоеSelf указатель на обработчик события, но ваш анонимный параметр не учитывает это, поэтому остальные параметры события будут испорчены.

Ответы на вопрос(3)

 // 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)^;

Я не знаю, что за$0C Магическое число существует для, но я держу пари, что все эти приведения там есть, потому что у вас было чертовски много времени, чтобы компилятор принял это. Он дал вам ошибки типа, говорящие о том, что вы не можете назначить одну вещь другой.

Эти ошибки типа существуют по причине! Если вы взломаете систему типов, все может сломаться. Попробуйте превратить этот анонимный метод в обычный метод TUploadThread и назначьте его таким образом, и посмотрите, не работает ли он лучше.

 TheDude04 мая 2012 г., 00:06
Спасибо тебе, Мейсон, видишь мой комментарий здесь
Решение Вопроса

Спасибо, ребята, я перешел на обычный метод, но я все еще получаю исключения в AddCookies (), последнее произошло в строке, которая читает FRWLock.BeginWrite; в этой процедуре TIdCookies.LockCookieList (AAccessType: TIdCookieAccess): TIdCookieList;

Если ваша ошибка является нарушением прав доступа сRead of address 00000000, это имеет очень специфическое значение. Это означает, что вы пытаетесь что-то сделать с объектом, который является Ноль.

Когда ты это получишь, перейди к отладчику. Если ошибка происходит в той строке, на которой вы сказали, что это происходит, то почти наверняка либоSelf илиFRWLock является Ноль на данном этапе. Проверьте обе переменные и выясните, какая из них еще не построена, и это укажет на решение.

 Remy Lebeau04 мая 2012 г., 01:17
Дайте код, который был показан, скорее всего,GlobalCookieManager объект не был создан до использования.

Не используйте анонимную процедуру для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; 
 TheDude04 мая 2012 г., 00:07
Спасибо, Реми, но у меня все та же проблема ... см. мой комментарий здесь
 Remy Lebeau04 мая 2012 г., 01:17
Посмотри мои другие комментарии.
 Remy Lebeau05 мая 2012 г., 17:37
Оказывается, чтоAddCookie() становится владельцем куки-файла, который ему передается. Таким образом, вы будете в конечном итоге с несколькимиTIdCookieManager объекты, ссылающиеся на те же физические объекты cookie. Это может привести к тому, что файлы cookie будут уничтожены. Я вижу два возможных решения: естьOnNewCookie вызовAddCookie() с копиейACookie вместо тогоACookie напрямую или позвониAddServerCookie(ACookie.ServerCookie) вместо тогоAddCookie().
 Remy Lebeau05 мая 2012 г., 17:45
Я отредактировал свой ответ.

Ваш ответ на вопрос