1

In a special diagnostics mode, I test for an internet connection by performing an HTTP GET operation on a known always-expected-to-be-live server. While this is normally very fast, this can be slow and/or timeout after 15s or more when working in a remote location or when the computer network adapter is disabled.

Is is possible to configure the Indy component so that I can interrupt it on demand? Or maybe there is a better way to perform a test HTTP GET operation? My code is below which I might call with HasInternet(http://master11.teamviewer.com/);

function HasInternet(strTestWebServer: String) : Boolean;
var
    idHTTP: TIdHTTP;
begin
    // Test an internet connection to the named server
    Result := False;
    idHTTP := TIdHTTP.Create(nil);
    if (idHTTP <> nil) then
        begin
        try
            try
                // Handle redirects in response from HTTP server. Not required for some servers,
                // but can confirm a connection to servers that redirect to https.
                idHTTP.HandleRedirects := True;

                // Add in the HTTP protocol header (if required) and retrieve the HTTP resource
                // Note: HTTP_PROTOCOL = 'http://';
                if (AnsiPos(HTTP_PROTOCOL, strTestWebServer) > 0) then
                    Result := (idHTTP.Get(strTestWebServer) <> '')
                else
                    Result := (idHTTP.Get(HTTP_PROTOCOL + strTestWebServer) <> '');
            except
                // HTTP protocol errors are sometimes returned by servers, with the status code
                // saved in TIdHTTP.ResponseCode. Common responses include:
                // * 403 Forbidden (request was valid, but server is refusing to respond to it)
                // * 404 Not Found (requested resource could not be found)
                // * 405 Method Not Allowed (requested method not supported by that resource)
                // These errors are counted as "valid connection to the internet possible"
                on E: EIdHTTPProtocolException do
                    // Status code can be found in "E.ReplyErrorCode" or "idHTTP.ResponseCode"
                    Result := True;
            else
                // All other exceptions are counted as definite failures
                Result := False;
            end;
        finally
            idHTTP.Free();
        end;
        end;
end;
AlainD
  • 5,413
  • 6
  • 45
  • 99

1 Answers1

2

You can interrupt a running TIdHTTP HTTP method by calling Disconnect from context of a thread different from the one in which the method was executed. With your design it would mean exposing the internally used TIdHTTP object in some way.

But for your task you might give e.g. the InternetCheckConnection function a try. For example for Delphi 7 it could be:

const
  FLAG_ICC_FORCE_CONNECTION = $00000001;

function InternetCheckConnectionA(lpszUrl: PAnsiChar; dwFlags: DWORD; dwReserved: DWORD): BOOL; stdcall;
  external 'wininet.dll' name 'InternetCheckConnectionA';

function InternetCanConnect(const URL: AnsiString): Boolean;
begin
  Result := Boolean(InternetCheckConnectionA(PAnsiChar(URL), FLAG_ICC_FORCE_CONNECTION, 0));
end;

function InternetNotConnected(const URL: AnsiString): Boolean;
begin
  Result := not Boolean(InternetCheckConnectionA(PAnsiChar(URL),
    FLAG_ICC_FORCE_CONNECTION, 0)) and (GetLastError = ERROR_NOT_CONNECTED);
end;
AlainD
  • 5,413
  • 6
  • 45
  • 99
Victoria
  • 7,822
  • 2
  • 21
  • 44
  • Thanks! Never knew about the `InternetCheckConnectionA` Windows function. Have just tested it. Where the network adapter(s) have been disabled in Control Panel, both methods still time out after 15s or more. But under normal circumstances where the adapter(s) are active, this method is much faster. – AlainD Aug 07 '17 at 13:10
  • Note that disconnecting a socket across threads does not always work on all platforms. And `InternetCheckConnection()` is known to report inaccurate results. At the very least, you could set `TIdHTTP`'s `ConnectTimeout` and `ReadTimeout` properties, and/or raise an exception in its `OnWork...` events. – Remy Lebeau Aug 07 '17 at 17:04
  • Also, performing a complete HTTP request/response pair is a bit overkill when you are only interested in checking for Internet connectivity. You could just use `TIdTCPClient` instead and connect to a well-known server port without actually communicating any data with it. – Remy Lebeau Aug 07 '17 at 19:50
  • @Remy, in Delphi 7 you don't need to worry about other platforms. But thanks for the info (we've discussed the same earlier as far as I remember). Btw. do you know about some reliable single Windows API function (if we stayed at Windows platform)? I'd be very glad if you answered this (I could flag this one to be deleted I hope ;-) – Victoria Aug 07 '17 at 19:55
  • 2
    @Victoria: there is no *single reliable* Win32 function for testing an Internet connection, as a machine can get Internet access from a number of different connections (LAN, Wifi, Mobile, proxy, VPN, etc). Testing them all gets harder over time. WinInet tried to solve it with `InternetGetConnectedState/Ex()` and `InternetCheckConnection()`, but they are not always accurate. Vista added `INetworkListManager::GetConnectivity()` and `INetwork::get_IsConnectedToInternet()`. Best way is to just connect to an Internet server and see if it succeeds or fails. Let Windows decide how to get there. – Remy Lebeau Aug 07 '17 at 20:34
  • @Remy, I would tend to do the same. I was just hoping that `InternetCheckConnection` connects to the given server. What it does if not connecting; does it only ping the server? I would really like to learn something new from a credible answer written by you. i can flag this one ;-) – Victoria Aug 07 '17 at 20:43
  • @Victoria: "*I was just hoping that `InternetCheckConnection` connects to the given server*" - supposedly it does, if `lpszUrl` is not NULL, yes. So, in that regard, maybe it is more accurate than `InternetGetConnectedState/Ex()`, at least (which is notoriously inaccurate). But it does also involve a server database, too, and if the database is down, who knows what happens. Also, the doc does say "ping", not "connect". If that is an actual ICMP ping, that is not always accurate over the Internet, since some servers do not respond to ICMP pings. I would use my own TCP socket, just in case. – Remy Lebeau Aug 07 '17 at 20:49
  • @Remy, thank you, I see, at least I hope :) _"I would use my own TCP socket, just in case"_ is closest to determine that your service is "online". You use the chosen library to connect to the target server(s) that are used, and if any of that fails you say "sorry, try it later because the server(s) are gone right now". Of course, that's what one cannot do by just checking the Internet connection. The point is what to do in case of used "teamviewer". Don't you want to post your own answer? (I'll reward you anyway, you helped me a lot in the past :) – Victoria Aug 07 '17 at 21:39