Jump to content
Der schöne Günther

Lookup for "localhost" takes 2 seconds

Recommended Posts

I am running a http server locally. I am connecting to it from the very same process.

I am very puzzled by the time it takes. To me, it seems that resolving the name "localhost" takes 2 seconds before it times out and defaults to "127.0.0.1":

 

Time taken for http://127.0.0.1: 42 ms
Time taken for http://localhost/: 2008 ms
Time taken for http://localhost/: 3 ms

This is the full source code:

 

program Project1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
	System.SysUtils,
	System.Net.HttpClient,
	System.Diagnostics,

	IdContext,
	IdHTTPServer,
	IdCustomHTTPServer;

type
	TServerClientTest = class
		private var
			client: THTTPClient;
			server: TIdHttpServer;
		private
			procedure handleServer(
				AContext: TIdContext;
				ARequestInfo: TIdHTTPRequestInfo;
				AResponseInfo: TIdHTTPResponseInfo
			);
		public
			constructor Create();
			destructor Destroy(); override;
			procedure Test(const url: String);
	end;


constructor TServerClientTest.Create();
begin
	inherited Create();

	server := TIdHTTPServer.Create(nil);
	server.Bindings.Add().Port := 80;
	server.OnCommandGet := handleServer;

	client := THTTPClient.Create();
end;

destructor TServerClientTest.Destroy;
begin
	server.Free();
	client.Free();
	inherited;
end;

procedure TServerClientTest.handleServer(
	AContext: TIdContext;
	ARequestInfo: TIdHTTPRequestInfo;
	AResponseInfo: TIdHTTPResponseInfo
);
begin
	AResponseInfo.ContentText := '<html><body><h1>Hello World</h1></body></html>';

	AResponseInfo.WriteHeader();
	AResponseInfo.WriteContent();
end;

procedure TServerClientTest.Test(const url: String);
var
	stopWatch: TStopwatch;
begin
	stopWatch := TStopwatch.StartNew();
	try
		server.Active := True;
		client.Get(url);
	finally
		stopWatch.Stop();
	end;

	WriteLn( String.Format('Time taken for %s: %.0f ms', [url, stopWatch.Elapsed.TotalMilliseconds]));
end;

var
	serverClientTest: TServerClientTest;
begin
	serverClientTest := TServerClientTest.Create();
	try
		serverClientTest.Test('http://127.0.0.1');
		serverClientTest.Test('http://localhost/');
		serverClientTest.Test('http://localhost/');
	finally
		serverClientTest.Destroy();
	end;
	ReadLn;
end.

Does anybody have an idea why this is?

Share this post


Link to post
Guest
server.Bindings.Add().Port := 80;

would be because you would be access by internet search... then the search go and back according with your net speed?

 

... I suppose!!!

Edited by Guest

Share this post


Link to post

Which operating system are we talking about?

2 seconds sounds like the default DNS client timeout.

Sometime around Windows 8 (needs clarification, I'm not sure), the following lines appeared in the Windows hosts file:

 

# localhost name resolution is handled within DNS itself.
#    127.0.0.1       localhost
#    ::1             localhost

 

You can try to uncomment these and give an other try. It's possible that your DNS settings needs to be checked / updated.

Share this post


Link to post

If your PC supports IPv6, using localhost will try both IPv6 and IPv4, so best to have your server listening on both 127.0.0.1 and ::1.  I recently changed the ICS TSimpleWebServer to do exactly this, for this reason.

 

You may think you can ignore IPv6, but life is not that simple for developers. 

 

Angus

 

Edited by Angus Robertson
  • Like 4
  • Thanks 1

Share this post


Link to post
1 hour ago, Angus Robertson said:

using localhost will try both IPv6 and IPv4, so best to have your server listening on both 127.0.0.1 and ::1

That did the trick, thank you!

 

Time taken for http://127.0.0.1: 28 ms
Time taken for http://localhost/: 6 ms
Time taken for http://localhost/: 5 ms

I hope I did that binding stuff correctly:

 

constructor TServerClientTest.Create();
var
	binding: TIdSocketHandle;
begin
	inherited Create();

	server := TIdHTTPServer.Create(nil);
	binding := server.Bindings.Add();
	binding.Port := 80;
	binding.IPVersion := TIdIpVersion.Id_IPv4;
	binding.IP := '127.0.0.1';

	binding := server.Bindings.Add();
	binding.Port := 80;
	binding.IPVersion := TIdIPVersion.Id_IPv6;
	binding.IP := '::1';

	server.OnCommandGet := handleServer;

	client := THTTPClient.Create();
end;

 

  • Like 2

Share this post


Link to post
Guest

WOW! then my tips is not so wrong... about a Internet intervention (maybe the DHCP.. I dont know 😂:classic_cheerleader:

Share this post


Link to post

If this application is for wider use, you need to make sure IPv6 is enabled on the PC, otherwise the binding will fail.  Many people disable IPv6 to quickly solve problems rather than fixing them properly.

 

Angus

 

  • Thanks 2

Share this post


Link to post

In ICS, there is a function that includes code attempting to open an IPv6 UDP socket, which fails if there are IPv6 addresses, you'd need something similar, if Indy does not have a similar function. 

 

            s := Ics_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
            Result := s <> INVALID_SOCKET;
            if Result then begin
                Ics_closesocket(s);
                GIPv6Available := 1;
            end
            else
                GIPv6Available := 0;

 

Angus

 

Share this post


Link to post

Adding a ipv6 binding causes Indy to call socket like this:

socket(AF_INET6, SOCK_STREAM, 0 {no protocol specified})

which is apparently fine with Windows as it returns a valid socket descriptor, although ipv6 is disabled. Maybe this is just a problem when UDP is used?

 

For me, everything seems fine. It doesn't seem I need to handle the "ipv6 was manually disabled" case. 😎

Share this post


Link to post

Enjoying the topic, is there any way to force the connection only via IPv4 in this example?

 

  LHTTPClient := THTTPClient.Create;
  try
    LHTTPResponse := LHTTPClient.Get(LURL); //Error sending data: 12002.Time limit exceed
    Result := LHTTPResponse.ContentAsString(TEncoding.UTF8);
  finally
    LHTTPClient.Free;
  end;

 

Because in a few cases, my clients who use my software end up receiving the error message described in the comment, and when I manually disable the IPv6 protocol in the Windows network settings, the software starts working again. =/
I understand that IPv6 must be misconfigured on the router or server on the client's network, however, by forcing the use of IPv4, I would no longer need to waste time with this type of problem that is not related to my software

Share this post


Link to post
3 hours ago, Rafael Dipold said:

is there any way to force the connection only via IPv4 in this example?

If you were using Indy's TIdHTTP, then it would automatically use IPv4 unless the requested URL is bracketed, eg: "http://[HostOrIPv6Address]/", which would force IPv6.

 

For the RTL's THTTPClient, I have no idea how it behaves in regards to IPv4/IPv6.

  • Like 2

Share this post


Link to post

I'll try, but the disadvantage of Indy is that I would have to include the SSL libraries and control the version of that too..

 

I believe it would be possible to implement this using THTTPClient by activating the option flag WINHTTP_OPTION_IPV6_FAST_FALLBACK

https://learn.microsoft.com/en-us/windows/win32/winhttp/option-flags
 

However, I believe that this could only be done by modifying Delphi's RTL by Embarcadero to include such an option...

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

×