Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2017-07

Avoid exception if FPHTTPClient cannot connect to server?

5 messages · 3 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Werner Pamler

Here is a little demo program which uses fphttpclient to download some file from some server.

If the URL exists everything is fine (note on Windows, that the OpenSSL dlls must be copied to the exe directory for the demo to work). If the URL does not exist, but the server responds, the program returns the well-known error 404 (if the 404 has been added to the response codes in HTTPMethod) - also fine. But if a connection to the server cannot be established -- maybe because the server URL is wrong -- then the program creates an exception. Synapse, on the other hand, does not crash on this occasion, it just returns an error 500. This behavior appears to be much more consistent than fpc's.

Is this intentional? Or should I report a bug?

// ------------------ snip ----------------------
program project1;
{$mode objfpc}{$H+}
uses
   Sysutils, Classes, fphttpclient;
function DownloadURL(URL: String; AStream: TStream; out AErrMsg:
String): Boolean;
begin
   AErrMsg := '';
   with TFPHTTPClient.Create(nil) do
     try
       AllowRedirect := true;
       HTTPMethod('GET', URL, AStream, [200, 404{, 500}]);
       Result := (ResponseStatusCode = 200);
       if not Result then
         AErrMsg := 'Error code ' + IntToStr(ResponseStatusCode) + ': '
+ ResponseStatusText;
     finally
       Free;
     end;
end;
const
   // existing URL  --> ok
   //URL =
'https://sourceforge.net/projects/freepascal/files/Documentation/3.0.2/doc-txt.zip/download';
   // Existing server, but not existing URL  --> error 404
   //URL =
'https://sourceforge.net/projects/freepascal/files/Documentation/3.0.2/doc-txt.zip/download-1';
   // Non-existing server
   URL =
'https://sourceforge-1.net/projects/freepascal/files/Documentation/3.0.2/doc-txt.zip/download-1';
var
   stream: TStream;
   errmsg: String;
begin
   stream := TFileStream.Create('test.zip', fmCreate);
   try
     if DownloadURL(URL, stream, errmsg) then
       WriteLn('Download of ' + URL + ' completed.')
     else
       WriteLn(errmsg);
   finally
     stream.Free;
   end;
   {$IFDEF WINDOWS}
   WriteLn('Press ENTER to quit.');
   ReadLn;
   {$ENDIF}
end.

Reply by email

Karoly Balogh (Charlie/SGR
to Werner Pamler

Hi,

Werner Pamler wrote 9 lines
If the URL exists everything is fine (note on Windows, that the OpenSSL dlls must be copied to the exe directory for the demo to work). If the URL does not exist, but the server responds, the program returns the well-known error 404 (if the 404 has been added to the response codes in HTTPMethod) - also fine. But if a connection to the server cannot be established -- maybe because the server URL is wrong -- then the program creates an exception. Synapse, on the other hand, does not crash on this occasion, it just returns an error 500. This behavior appears to be much more consistent than fpc's.

I disagree. I think generating any HTTP error code locally, which should always come from the server is anything but a good idea. It could mask true server issues from network connectivity issues, which distinction could be still relevant and the client application might want to employ different error mitigation techniques to solve one or the other case.

Is this intentional? Or should I report a bug?

The code in FPHTTPClient has an explicit Raise line when the connection is unsuccessful. So I'd say this is not a bug, but by design. Maybe this behavior should be better documented or the Exception itself could be more descriptive tho', but I haven't tried this.

(Note that I generally dislike APIs which are designed to throw exceptions, but anyway, in this case it looks like it's by design and it's still better than just generating a random HTTP code locally.)

Charlie

Reply by email

Michael Van Canneyt
to Werner Pamler: Here is a little demo program which uses fphttpclient to download some file from some…
Werner Pamler wrote 14 lines
Here is a little demo program which uses fphttpclient to download some file from some server. If the URL exists everything is fine (note on Windows, that the OpenSSL dlls must be copied to the exe directory for the demo to work). If the URL does not exist, but the server responds, the program returns the well-known error 404 (if the 404 has been added to the response codes in HTTPMethod) - also fine. But if a connection to the server cannot be established -- maybe because the server URL is wrong -- then the program creates an exception. Synapse, on the other hand, does not crash on this occasion, it just returns an error 500. This behavior appears to be much more consistent than fpc's. Is this intentional? Or should I report a bug?

This is very intentional.

Returning a 500 on connection error is plain wrong. Status 500 indicates an internal error on the server. This is an 'agreement' governed by a RFC.

But if the connection cannot be made, there was no server, and there cannot be a server status either.

Michael.

Reply by email

Werner Pamler
to Karoly Balogh (Charlie/SGR: I disagree. I think generating any HTTP error code locally, which should always come from…

Agreed. This is the same situation as with string-to-number conversion: StrToFloat fires an exception if the string is not a valid float, but there's also a "TryStrToFloat" which by-passes the exception and just returns a false in case of an error. Why not having something like a "TryGet" which returns true only if the file is successfully downloaded and false in all other cases (HTTP error, or no connection)?

Reply by email

Michael Van Canneyt
to Werner Pamler
Werner Pamler wrote 6 lines
Agreed. This is the same situation as with string-to-number conversion: StrToFloat fires an exception if the string is not a valid float, but there's also a "TryStrToFloat" which by-passes the exception and just returns a false in case of an error. Why not having something like a "TryGet" which returns true only if the file is successfully downloaded and false in all other cases (HTTP error, or no connection)?

Because that would require to completely change the interface and possibly implementation of underlying/intermediate methods, which rely now on an exception being thrown.

It's of course possibly to make a TryGet which calls get and catches the exception, but you can make such a function yourself, I really don't see the value: in my experience, you usually want to know why the call failed, and then the exception is useful. A boolean value of 'false' is not very descriptive.

Michael.

Reply by email