Jump to content

Mark Lobanov

Members
  • Content Count

    18
  • Joined

  • Last visited

Posts posted by Mark Lobanov


  1. Hello
    I have a Win64 ServiceApplication (no GUI) with separate thread which received a data from specific web-service.

    TSslHttpCli created and initialized within the thread constructor

      fHc := TSslHttpCli.Create( nil );
      fHc.RcvdStream := TBytesStream.Create;
      fHc.SendStream := TBytesStream.Create;
      fHc.OnBeforeHeaderSend := hcBeforeHeaderSendHandler;
      fHc.OnCookie := hcOnCookieHandler;
      fHc.SocketFamily := sfAny;
      fHc.RequestVer := '1.1';
      fHc.MultiThreaded := True;
      
      fHc.sslContext := TSslContext.Create( nil );
        fHc.sslContext.SslMinVersion := sslVerTLS1_2;
        fHc.sslContext.SslMaxVersion := sslVerMax;
        fHc.sslContext.SslCliSecurity := TSslCliSecurity.sslCliSecTls12;
        fHc.sslContext.SslVersionMethod := sslBestVer_SERVER;
    
      fHc.Accept := MC_HTTP_ACCEPT_VALUE;
      fHc.Connection := MC_HTTP_CONNECTION_VALUE;
      fHc.Agent := MC_HTTP_USER_AGENT_VALUE;
      fHc.NoCache := True;
      fHc.ResponseNoException := True;


    In CallerThread.Execute a call a GET method in synchronous mode with OpenSsl 3.2.0.

    At first glance all works fine but a have a lot of errors "Request aborted on timeout". Remote web service is quite stable.

    Maybe I missed something?
    Please, help me.

     


  2. 32 minutes ago, Angus Robertson said:

    So effectively you want to use an external pool of TSslHttpCli objects from within your threads, rather than creating them as needed within the threads?

    I have a similar pattern but easier
    One session, one client data (metadata, authorization, cookie etc.), one TSslHttpCli objects per session lifetime. No pool, no concurrent access to TSslHttpCli objects.

    My problem is that the TSslHttpCli object is created in one thread and is used each time in another thread. On each second call TSslHttpCli objects freezes in httpDnsLookup state.

    I do not want to use the asynchronous mode of the object yet. DelphiMVCFramework endpoint call is already in asynchronous mode within separate thread.

    Besides I don't have access to this threat execute method to add custom message handler.


    I added ThreadAttach/ThreadDetach code (look start message in this topic) and freezes gone but I not sure that this code is safe. François Piette thinks this is the wrong code


  3. Thank you, i saw the topic

     

    but is not my case.


    I can't create, run and free TSslHttpCli object in the same thread context for performance reasons and business flow.

    I have to create and free TSslHttpCli object within DelphiMVCFramework session and reuse it each time my endpoints are called.

    Each time my endpoint is called, new thread is created by DelphiMVCFramework engine with TTask.Run() and TSslHttpCli object is used in that thread context, each endpoint call - new thread, quite so DelphiMVCFramework works.

    I also can't use DelphiMVCframework's embedded HttpClient because it doesn't support TLS1.3 ((

     

     


  4. Since the code of [using] section is already executed in a separate thread, I use the synchronous Get method and not GetAsync
    I expect that in the finally section work of Get method will be completed
     

    1 hour ago, FPiette said:

    At first glance it is not correct.

     

    What do you think would be the correct solution?
     


  5. Hello
    I don't know, why finalization code from OverbyteIcsLIBEAY unit called earlier than TSslBaseComponent.FinalizeSsl and where are many exceptions occur in FinalizeSsl when my windows service stops ?
    What could be the reasons for this case?
    ICS 8.70, Delphi Tokyo


  6. Hello
    I use TSslHttpCli in my backend built on DelphiMVCFramework for query external services.

    The TSslHttpCli object embedded into DelphiMVCFramework session and used each time then client called my endpoint.

    This call each time occurs in different thread. For certain reasons a can't create, use and free TSslHttpCli object each time in one thread context.

     

    I want to use thread context switching. Is this code safe?

    // initialization
    fClient: TSslHttpCli;
    ...
    fClient := TSslHttpCli.Create( nil );
    fClient.MultiThreaded := True;
    
    
    // using
    if fClient.ThreadID = 0 then
      fClient.ThreadAttach;
    try
      fClent.Post(...);
    finally
      fClient.ThreadDetach;
    end;

     


  7. unit OverbyteIcsMailQueue
    
    constructor TIcsMailQueue.Create(Aowner: TComponent);
    begin
        inherited;
    
    ...
    
    {$IFDEF UNICODE}
        FBodyText.WriteBOM := False;  { V8.67 }
    {$ENDIF UNICODE}
        FMailServers := TMailServers.Create(Self);
    ...
    
    end;
    
    procedure TIcsMailQueue.SaveQuHdrs;
    ...
    {$IFDEF UNICODE}
        QueueLines.WriteBOM := False;  { V8.67 }
    {$ENDIF UNICODE}
    ...

    [DCC Error] OverbyteIcsMailQueue.pas(1464): E2003 Undeclared identifier: 'WriteBOM'
    [DCC Error] OverbyteIcsMailQueue.pas(2243): E2003 Undeclared identifier: 'WriteBOM'


  8. Hello

     

    I'm trying to use TSslHttpCli in multithreading environment (Parallel Programming Library) and getting an error ESocketException with message 'Invalid argument (#10022 in WSACancelAsyncRequest)’ in Get method.

    Please, help me. Why does an error occurs?

    If I'm calling TSslHttpCli.Get in main application thread this error does not occurs.

    program ics_test;
    
    {$APPTYPE CONSOLE}
    
    
    uses
      Classes,
      IOUtils,
      System.Threading,
      System.SyncObjs,
      System.SysUtils,
      OverbyteIcsWndControl,
      OverbyteIcsHttpProt,
      OverbyteIcsWSocket;
    
    type
      TSslHttpCliHelper = class
      strict private
        hc: TSslHttpCli;
        fCookie: string;
        procedure hcOnCookie(Sender: TObject; const Data: String; var Accept: Boolean);
        function handleHttpResult: string;
      public
        procedure doGet;
        constructor Create;
        destructor Destroy; override;
      end;
    
      TTestTask = class(TTask)
      private
        fHelper: TSslHttpCliHelper;
      public
        constructor Create(Sender: TObject; Event: TNotifyEvent; const AProc: TProc;
          const APool: TThreadPool; const AParent: TTask);
        destructor Destroy; override;
      end;
    
    
    function makeFileNameID(Obj: TObject): string;
    begin
      Result := IntToHex(Integer( pointer( Obj ) ), 8);
    end;
    
    
    { TTestTask }
    
    constructor TTestTask.Create(Sender: TObject; Event: TNotifyEvent; const AProc: TProc;
          const APool: TThreadPool; const AParent: TTask);
    begin
      inherited Create(Sender, Event, AProc, APool, AParent);
      fHelper := TSslHttpCliHelper( Sender );
    end;
    
    
    destructor TTestTask.Destroy;
    begin
      fHelper.Free;
      inherited;
    end;
    
    { TSslHttpCliHelper }
    
    constructor TSslHttpCliHelper.Create;
    begin
      hc := TSslHttpCli.Create( nil );
      hc.ContentTypePost := 'application/json';
      hc.Accept := '*/*';
      hc.Connection := 'Keep-Alive';
      hc.Agent := 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36';
      hc.ResponseNoException := True;
    //  hc.MultiThreaded := True;
      hc.SendStream := nil;
      hc.RcvdStream := TBytesStream.Create;
      hc.OnCookie := hcOnCookie;
    
      hc.sslContext := TSslContext.Create( nil );
      hc.sslContext.SslMinVersion := sslVerTLS1_2;
      hc.sslContext.SslMaxVersion := sslVerTLS1_3;
    end;
    
    destructor TSslHttpCliHelper.Destroy;
    begin
      hc.SendStream.Free;
      hc.RcvdStream.Free;
      hc.SslContext.Free;
      hc.Free;
      inherited;
    end;
    
    procedure TSslHttpCliHelper.doGet;
    begin
      hc.url := 'https://api.ehealth-ukraine.org/api/dictionaries';
      hc.Get;
      TFile.WriteAllText('test.'+makeFileNameID( Self )+'.json', handleHttpResult, TEncoding.UTF8);
    end;
    
    function TSslHttpCliHelper.handleHttpResult: string;
    begin
      if Assigned( hc.RcvdStream ) and (hc.RcvdStream.Size > 0)
        then Result := Trim( TEncoding.UTF8.GetString( TBytesStream( hc.RcvdStream ).Bytes ) )
        else Result := EmptyStr;
    end;
    
    procedure TSslHttpCliHelper.hcOnCookie(Sender: TObject; const Data: String; var Accept: Boolean);
    begin
      fCookie := Data;
      Accept := True;
    end;
    
    function createTask(ATaskHelper: TSslHttpCliHelper): TTestTask;
    begin
      Result := TTestTask.Create(ATaskHelper, nil,
        procedure()
        begin
          ATaskHelper.doGet;
        end,
        TThreadPool.Default, nil);
    end;
    
    var tasks: array of ITask;
        task: ITask;
        hlp: TSslHttpCliHelper;
    
    begin
    {
    // Normal
      hlp := TSslHttpCliHelper.Create;
      try
        hlp.doGet;
      finally
        hlp.Free;
      end;
    }
    
    
    {
    ---------------------------
    Debugger Exception Notification
    ---------------------------
    Project ics_test.exe raised exception class ESocketException with message 'Invalid argument (#10022 in WSACancelAsyncRequest)'.
    ---------------------------
    Break   Continue   Help
    ---------------------------
    }
      SetLength(tasks, 1);
      tasks[0] := createTask( TSslHttpCliHelper.Create );
    
      for task in tasks do
          task.Start;
    
      TTask.WaitForAll( tasks );
    
      SetLength(tasks, 0);
    end.

     

    I use Delphi Tokyo 25.0.29899.2631 and ICS 8.61

    An example in attachment.

    ics_test.zip

×