Jan Doggen
Jan Doggen

Reputation: 9096

How to get at response headers from THTTPReqResp?

I have some old code using THTTPReqResp, this calls Execute for SOAP requests against Exchange Web Services.
I'm modifying it to (also) use OAuth login. This works fine, but I'm having trouble detecting when an access token has expired.

In a test app using a TipwHTTP component from nSoftware IP*Works I can go through the response headers to detect the one indicating an expired token:

with ipwHTTP do
  try
     PostData := ABody;
     Post(cBaseURL);
  except
      on e:Exception do
      begin
         // A special header is returned when the token has expired:
         //      Header x-ms-diagnostics: 2000002;reason="The token has expired.";error_category="invalid_lifetime"
         for l := 0 to ParsedHeaders.Count-1 do
            if (Pos('x-ms-diagnostics',ParsedHeaders[l].Field) <> 0)
            and (Pos('2000002',ParsedHeaders[l].Value) <> 0) then
            begin
               FTokenExpired := true;
               Break;
            end;
         ...   
      end;
  end;

Where/how can I access the headers when using THTTPReqResp or THTTPReqResp.HTTP?

That component contains a HTTP property of type THTTPClient but I'm getting nowhere with that. The documentation Using an HTTP Client states that THTTPClient has OnRequestError and OnRequestcompleted events but those are nowhere to be found in the code for THTTPClient or its class helper. The documentation for these events refer to System.Net.HttpClientComponent.TNetHTTPRequest not System.Net.THTTPClient, and there is no HttpClientComponent in System.Net.

Notes:

Upvotes: 1

Views: 1794

Answers (1)

Peter Wolf
Peter Wolf

Reputation: 3830

As you have found out THTTPReqResp underwent shotgun surgery in Delphi 10.3. Originally it was using WinINet on Windows and Indy on Linux under the hood, but they changed it to THTTPClient (to support mobile platforms, I guess).

In this new implementation none of THTTPReqResp methods (Get, Execute) and events (OnBeforePost, OnReceivingData) expose Request or Response objects.

There, however, is a chance to gain access to Response object via global OnHttpError handler, if server responds with an error HTTP status code (>= 300). Use procedure Soap.SOAPHTTPTrans.SetOnHttpError to install the global handler. It has HTTPResponse: IHTTPResponse as its second parameter, which lets you inspect returned headers. If the server responds with 1xx or 2xx status then you're out of luck and should consider implementing custom THTTPReqResp descendant or migrating to more appropriate HTTP client implementation (e.g. THTTPClient directly, or similar).

{ assuming HTTPReqResp1: THTTPReqResp is a component on TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  SetOnHttpError(HTTPReqRespError);
end;

procedure TForm1.HTTPReqRespError(const HTTPReqResp: THTTPReqResp;
  const HTTPResponse: IHTTPResponse; const Error: ESOAPHTTPException;
  var Action: TSOAPHttpErrorAction);
begin
  if (HTTPReqResp = HTTPReqResp1) and StartsText('2000002', HTTPResponse.HeaderValue['x-ms-diagnostics']) then
  begin
    FTokenExpired := True;
    Action := TSOAPHttpErrorAction.heaAbort; { or whatever }
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FTokenExpired := False;
  try
    { ... }
    { HTTPReqResp1.Get or HTTPReqResp1.Execute or whatever }
    { ... }
  except
    if not FTokenExpired then
      raise;
    { handle token expiration here  }
  end;
end;

In my personal opinion this is pretty ugly way to handle such cases and I just can't understand why they introduced global handler in new code, which affects all instances of THTTPReqResp. I'm not impressed with this new design at all.

Eagle eye: Have you noticed character case inconsistency between THTTPReqResp (THTTPClient, ESOAPHTTPException) and SetOnHttpError (TSOAPHttpErrorEvent, TSOAPHttpErrorAction)?

Upvotes: 1

Related Questions