Jump to content
Mischir

Problem to reuse session with TSslHttpRest

Recommended Posts

Hello,.

I am currently trying to read data from Salesforce via REST Api. There is a working example for Postman that I would like to implement for a project in Delphi (12) / ICS 9.2

 

The process is as follows:

1) Sending the certificate (PFX) + password to the host
2) The server sends back session cookies when the certificate has been verified.
3) Sending the client ID and secret together with the session cookies from the previous session
4) In the response header is a code "Location: xyz"
5) This code is then used to request a token.
6) You receive the token and can then make requests via the REST API.

 

1) and 2) work exactly the same as in Postman.
My problem is that the session cookies do not seem to be transferred correctly and a new session is created under 3) and no code is sent. It must be the same session.
If I copy the "Location code" from Postman and paste it into the Delphi application, 5) and 6) also work.


I have also uploaded the logging in the attachment.
I think this is the problem:
14:20:28:879 NSCB> New session created
14:20:28:879 000001CB1A118B70 CliNewSessionCB [000001CB1B664740] Reused: False

The session is no longer used. In Postman there stands in the console : TLS: reused=true

 

Here is my Code in Delphi. I have added a few lines of code for logging (Memo and ICSLogger)

 

Thanks, if anyone finds a mistake or something is missing.

Mike

 

procedure TForm1.Button6Click(Sender: TObject);
var
  HTTPRest: TSslHttpRest;
  SslContext: TSslContext;
  CookieManager: TIcsCookies;
  ResponseStream: TMemoryStream;
  ResponseHeaders: TStringList;
  Cookies: TStringList;
  Url: string;
  ResponseText: TStringList;
  i: Integer;
  errs: string;
  CookieHeader: string;
begin

  if OpenDialog1.Execute then
  begin
    ED_PFX_Cert_Dir.text := OpenDialog1.FileName;
   // clear Memo
    mResult.Clear;

    try

      CookieManager := TIcsCookies.Create(self);
      SslContext := TSslContext.Create(self);
      HTTPRest := TSslHttpRest.Create(self);

      try
        ResponseStream := TMemoryStream.Create;
        ResponseHeaders := TStringList.Create;
        Cookies := TStringList.Create;
        ResponseText := TStringList.Create;

        SslContext.SSLVerifyPeer := true;
        SslContext.SslVerifyPeerModes := [SslVerifyMode_PEER];

        SslContext.SslSessionCacheModes := SslContext.SslSessionCacheModes + [sslSESS_CACHE_CLIENT]; 

        HTTPRest.SslContext := SslContext;
        HTTPRest.OnSslVerifyPeer := SslHttpRest1SslVerifyPeer; 
        HTTPRest.CertVerMethod := CertVerWinStore;
        HTTPRest.RestCookies.AutoSave := true; // necessary, because I use cookiemamager?

        HTTPRest.Connection := 'keep-alive';

        CookieManager.AutoSave := true;

        HTTPRest.RestCookies := CookieManager;

        // Sending certificate
        HTTPRest.SslCliCert.LoadFromFileEx(ED_PFX_Cert_Dir.text, croYes, croTry, Ed_PW_Cert.text, errs);
        if errs <> '' then
          mResult.Lines.AddStrings(errs);

        // Logger
        if Form1.Chb_logging.Checked then
        begin
          IcsLogger1.LogFileName := TPath.GetSharedDocumentsPath + PathDelim + 'ICSLogger.txt';
          HTTPRest.IcsLogger := IcsLogger1;
          HTTPRest.DebugLevel := DebugHdr;
        end;


        // Set up SSL context with client certificate, necessary ???

        SslContext.SslCertFile := ED_PFX_Cert_Dir.text;
        SslContext.SslPassPhrase := Ed_PW_Cert.text;

        HTTPRest.Url := ED_Url.text + '/nidp/app/login';
        HTTPRest.RcvdStream := ResponseStream;

        HTTPRest.FollowRelocation := true;
        HTTPRest.SslSessCache := true;

// First request to get session cookies
        try
          HTTPRest.Get;
          mResult.Lines.Add('HTTP GET request succeeded.');
        except
          on E: Exception do
          begin
            mResult.Lines.Add('Error with connection: ' + E.Message);
          end;
        end;

        mResult.Lines.Add(Format('Response Code: %d', [HTTPRest.StatusCode]));
        mResult.Lines.Add(Format('Response Text: %s', [HTTPRest.ReasonPhrase]));

        // Extract cookies from response headers
        for i := 0 to HTTPRest.RcvdHeader.Count - 1 do
        begin
          if Pos('Set-Cookie:', HTTPRest.RcvdHeader[i]) > 0 then
          begin
            Cookies.Add(Copy(HTTPRest.RcvdHeader[i], Length('Set-Cookie: ') + 1, MaxInt));

          end;
        end;

        // Output the response headers
        ResponseHeaders.text := HTTPRest.RcvdHeader.text;
        mResult.Lines.AddStrings(ResponseHeaders);

        // Output the response content
        ResponseStream.Position := 0;
        ResponseText.LoadFromStream(ResponseStream);

        // Prepare the next request
        Url := 'https://XYZ.com/nidp/oauth/nam/authz';
        HTTPRest.Url := Url + '?response_type=code&client_id=' + ED_ClientID.text +
          '&redirect_uri=https://localhost:443&scope=apim';

        ED_Request.text := HTTPRest.Url;

        // Add cookies to the header of the second request

        // Loop through all cookies
        for i := 0 to CookieManager.Count - 1 do
        begin
          // Construct the cookie header string
          if CookieHeader <> '' then
            CookieHeader := CookieHeader + '; ';
          CookieHeader := CookieHeader + CookieManager.Get1Cookie(i).CName + '=' + CookieManager.Get1Cookie(i).CValue;

        end;

        // Add the cookie header to the ExtraHeaders

        if CookieHeader <> '' then
          HTTPRest.ExtraHeaders.AddPair('Cookie', CookieHeader);

        mResult.Lines.Add('Cookies from first request:');
        mResult.Lines.Add(CookieHeader);

        // Log the final URL and headers
        mResult.Lines.Add('Final request URL: ' + HTTPRest.Url);
        mResult.Lines.Add('Final request headers:');
        mResult.Lines.AddStrings(HTTPRest.ExtraHeaders);

        HTTPRest.FollowRelocation := false;  // Important !

// Second request , to get the "code" in HEADER "Location". 
        try
          HTTPRest.Get;
          mResult.Lines.Add('HTTP GET2 request succeeded.');
          mResult.Lines.Add(Format('Response Code: %d', [HTTPRest.StatusCode]));
          mResult.Lines.Add(Format('Response Text: %s', [HTTPRest.ReasonPhrase]));

          // Loop through the headers and show them
          for i := 0 to HTTPRest.RcvdHeader.Count - 1 do
            mResult.Lines.Add(HTTPRest.RcvdHeader[i]);

          // Output the response content
          ResponseStream.Position := 0;
          ResponseText.LoadFromStream(ResponseStream);
          mResult.Lines.AddStrings(ResponseText);

        except
          on E: Exception do
          begin
            mResult.Lines.Add('Error with connection: ' + E.Message);
          end;
        end;

      finally
        ResponseHeaders.Free;
        ResponseStream.Free;
        Cookies.Free;
        ResponseText.Free;

      end;

    finally

      SslContext.Free;
      CookieManager.Free;
      HTTPRest.Free;
    end;
  end;
end;

 

ICSLogger.txt

Share this post


Link to post

Not sure why you wrote such complicated code, none of our examples look like that for TSslHttpRest, only the old THttpCli component

.

 

TSslHttpRest includes TSslContext, TIcsCookies and TMemoryStream so they are all redundant.  The IcsLogger is also unnecessary, since the component has logging built in, using the OnHttpRestProg event.

 

The OverbyteIcsSnippets sample has a function doHttpRestReqClick that makes a request with only a few lines of code.

 

But before writing any code, I would use the OverbyteIcsHttpRestTst sample to test the URL and parameters. 

 

Angus

Share this post


Link to post
1 hour ago, Angus Robertson said:

Not sure why you wrote such complicated code, none of our examples look like that for TSslHttpRest, only the old THttpCli component

.

 

TSslHttpRest includes TSslContext, TIcsCookies and TMemoryStream so they are all redundant.  The IcsLogger is also unnecessary, since the component has logging built in, using the OnHttpRestProg event.

 

The OverbyteIcsSnippets sample has a function doHttpRestReqClick that makes a request with only a few lines of code.

 

But before writing any code, I would use the OverbyteIcsHttpRestTst sample to test the URL and parameters. 

 

Angus

Hi Angus,
Thanks for the tip! You saved my day 🙂
I used the code snippet from the OverbyteIcsSnippets doHttpRestReqClick and customized it a bit and it all works... and with just a few lines of code!

Thanks for the quick support
Regards,
Mike

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×