2

I must be overlooking something....
Why doesn't the NetHTTPClientRequestError get triggered here?

type
   TUpdateChecker = class
      private
         var FHTTP  : TNetHTTPClient;
         var FAbort : Boolean;
         procedure NetHTTPClientRequestError(const Sender: TObject; const AError: string);
      public
         constructor Create;
         destructor Destroy; override;
         function CheckNewVersion(var ANewVer: String): Boolean;
      end;

{ TUpdateChecker }

constructor TUpdateChecker.Create;
begin
   inherited;
   FHTTP := TNetHTTPClient.Create(nil);
   FHTTP.OnRequestError := NetHTTPClientRequestError;  // Error handler assigned here
   FHTTP.ConnectionTimeOut := 10000;
end;

destructor TUpdateChecker.Destroy;
begin
   FHTTP.Free;
   inherited;
end;

function TUpdateChecker.CheckNewVersion(var ANewVer: String): Boolean;
var
   lJSONStr: String;
begin
   Result := false;
   try
      lJSONStr := FHTTP.Get(cUpdateJSON + 'sdhgfy').ContentAsString;  // Add some junk to cause error
   except
      on E:Exception do   
      begin
         // We don't get at the breakpoint here  
         WriteErrorLog(TSettings.DataLocation + cErrLogFile, 'CheckNewVersion: ' + E.Message);
         Exit;
      end;
   end;

   if FAbort then Exit;  // FABort = false here

   // Rest of code removed
end;

procedure TUpdateChecker.NetHTTPClientRequestError(const Sender: TObject; const AError: string);
begin
   // We don't get at the breakpoint here, FAbort never becomes true
   ShowMessage('ERROR Sender.ClassName=' + Sender.ClassName + ' AError=' + AError);
   FAbort := true;
end;

Calling code from a FormShow:

 lCheck := TUpdateChecker.Create;
 try
    if lCheck.CheckNewVersion(lNewVer) then
    begin
       LblNewVersion.Caption := Format(LblNewVersion.Caption,[lNewVer]);
       LblNewVersion.Visible := true;
       LblUpgrade.Visible    := true;
    end;
 finally
    lCheck.Free;
 end;

This is a Win32 app running on Win10.

cUpdateJSON is a valid URL to a JSON file on my website. I added the 'sdhgfy' junk to cause the error. My attention is to catch both 'common' HTTP status code like 500, 404, as well as exceptions.

Jan Doggen
  • 8,799
  • 13
  • 70
  • 144
  • What is the value of `cUpdateJSON`? The HTTP component probably return an error for a network error (no server at the address give) but not at the application level (HTTP error such as 404 which is returned in a status code). What is the answer returned to a browser using the exact same URL? – fpiette Apr 14 '21 at 19:29
  • @fpiette See edit at and of question – Jan Doggen Apr 14 '21 at 20:05

1 Answers1

3

Because :

lJSONStr := FHTTP.Get(cUpdateJSON + 'sdhgfy').ContentAsString; 

is executing successfully.

Whatever cUpdateJSON is, it points to a valid server that is returning data to you, even though you have appended some garbage. It won't be the data you expect, but it will be data nevertheless, so the error is not raised.

You will need to validate the returned data to be sure that the server returned what you expected. NetHTTPClientRequestError will only handle cases where the URL specified fails to connect, etc (transport, socket, and protocol level exceptions). It knows nothing about whether or not the service on the other end was able to handle your specific request or not. It delivered your request successfully and the server returned a response. That's all it cares about.

If you want to check the server response you can inspect the StatusCode from the returned IHTTPResponse before saving its content into a string:

function TUpdateChecker.CheckNewVersion(var ANewVer: String): Boolean;
const
  HTTP_OK = 200;
var
  lResp : IHTTPResponse;
  lJSONStr: String;
begin
   Result := false;
   try
      lResp := FHTTP.Get('http://www.google.com/thiswontwork');

      // Note by OP: *if* the NetHTTPClientRequestError gets triggered
      // we have serious errors like e.g. invalid certificates
      // and lResp.StatusCode will give an AV. Therefore:
      if FAbort then Exit;

      if lResp.StatusCode <> HTTP_OK then begin
        // handle me!
      end else
        lJSONStr := lResp.ContentAsString();
   except
      on E:Exception do
      begin
        WriteErrorLog(TSettings.DataLocation + cErrLogFile,
                      'CheckNewVersion: ' + E.Message);
        Exit;
      end;
   end;

   if FAbort then Exit;  // FABort = false here

   // Rest of code removed
end;
Jan Doggen
  • 8,799
  • 13
  • 70
  • 144
J...
  • 30,968
  • 6
  • 66
  • 143
  • Ouch ;-) That is correct. It's HTML saying "The requested URL was not found on this server. Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request." So I would have to use an OnRequestCompleted handler or even an additional THTTPNetRequest as well to get more detailed info like HTTP status? – Jan Doggen Apr 14 '21 at 20:10
  • 1
    @JanDoggen I've expanded. – J... Apr 14 '21 at 20:32
  • I have added a 'real-life' note to your sample code. That saves the next reader some effort – Jan Doggen Apr 15 '21 at 17:40
  • @JanDoggen Sure - I didn't really tidy any of it up, but fair. Figured you knew what you were doing! ;) – J... Apr 15 '21 at 17:54