2012-05-03 23 views
2

My Delphi 2010アプリケーションはマルチスレッドを使用してアップロードします。アップロードされたデータはログインが必要なPHP/Webアプリケーションにポストされます。共有/グローバルクッキーマネージャーを使用する必要がありますIndy10リビジョン4743)を使用して「メートルTIdCookieManagerは:(-スレッドセーフされていないためIndyのグローバルでスレッドセーフなクッキーマネージャー

また、サーバ側では、セッションIDは、自動的に5分ごとに再生成されるので、私はグローバル&ローカルクッキーの両方を維持する必要があります管理者は同期しています

私のコードは次のようになります:

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; 

しかし、動作しません! ()

プロジェクトMyEXE.exe「のアドレス00000000の読み取りアドレス00000000でアクセス違反」というメッセージ で例外クラスのEAccessViolationを上げAddCookiesを呼び出すときに、私はこの例外を取得しています。

私もassign()を使って試しました。

TheCookieManager.CookieCollection.Assign(GlobalCookieManager.CookieCollection); 

しかし、私はまだ、通常はここで、同じ例外を取得:

TIdCookieManager.GenerateClientCookies() 

誰もがこの問題を解決する方法を知っていますか?

+2

あなたはOnNewCookie割り当てで何をやっていますか?私がポインタキャストの複数のレイヤーを見て、匿名メソッドをラップし、 'end)^)+ $ 0C)^;'のようなもので終わると、少し神経質になります。 –

+0

私はそれが最高のコードではないことに同意しますが、コードで書いたように、OnNewCookieはローカルとグローバルの両方のCookieマネージャを同期させて保持します(そして、問題がOnNewCookieイベントでない限り、私にはわかります)。 – TheDude

+2

私は@MasonWheelerに同意します。 OnNewCookie'イベントは、オブジェクトインスタンスの非静的メソッドであり、匿名プロシージャではありません。 'TIdCookieManager'は隠れた' Self'ポインタをイベントハンドラに渡しますが、あなたの匿名パラメータはそれを考慮していないので、残りのイベントパラメータはうまくいきません。 –

答えて

2

は皆さんありがとう

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; 

次に、このようにそれを使用します、私は通常の方法に変換したが、私はまだ得ている AddCookies()内の例外、最後の行が読み取られる行に発生しました FRWLock.BeginWrite;この手順では、 TIdCookies。LockCookieList(AAccessType:TIdCookieAccess): TIdCookieList;

エラーがRead of address 00000000のアクセス違反である場合、それは非常に具体的な意味を持っています。これは、オブジェクトがの場合、で何かをしようとしていることを意味します。

これを取得したら、デバッガを起動してください。あなたが起こっていると言った行でエラーが発生した場合は、SelfまたはFRWLockのいずれかがで、この時点ではであることがほぼ確実です。両方の変数をチェックして、どちらがまだ構築されていないかを把握してください。

+0

表示されているコードが与えられていると、 'GlobalCookieManager'オブジェクトは使用される前にインスタンス化されていない可能性が非常に高いです。 –

3

私は推測していた場合、私はあなたの問題はここにどこかにあると言うだろう:私は$0Cマジックナンバーがためにそこにあるかわからないんだけど

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

が、私はこれらすべてのキャストがある賭けるありあなたがこれを受け入れるためにコンパイラーに手を差し伸べる時間があったからです。あるタイプのものを他のタイプのものに割り当てることができなかったというエラーがタイプされました。

これらのタイプのエラーは、理由があります!あなたがタイプシステムの周りにあなたのやり方をハックすれば、事態は非常に壊れそうです。その匿名メソッドをTUploadThreadの通常のメソッドに変換し、それをその方法で割り当てて、それがうまく動作しないかどうかを確認してください。

+0

メーソンありがとう、[私のコメントはこちら](http://stackoverflow.com/questions/10439511/global-thread-safe-cookies-manager-with-indy#comment13477337_10439511) – TheDude

5

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.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; 
+0

レミー、ありがとう、私はまだ同じ問題...私のコメントを参照してください(http://stackoverflow.com/questions/10439511/global-thread-safe-cookies-manager-with-indy#comment13477337_10439511) – TheDude

+0

私の他のコメントを参照してください。 –

+1

'AddCookie()'は、渡されたクッキーの所有権を取ります。ですから、同じ物理的なクッキーオブジェクトを参照する複数の 'TIdCookieManager'オブジェクトで終わるでしょう。それはクッキーがゴミ箱になる原因になります。 'OnNewCookie'を' ACookie'の代わりに 'ACookie'のコピーで' AddCookie() 'を直接呼び出すか、' AddCookie() 'の代わりに' AddServerCookie(ACookie.ServerCookie) 'を呼び出すという2つの解決策があります。 –

関連する問題