views:

562

answers:

3

Hi all, I seem to have found a discrepancy when testing ASP.NET applications locally on the built-in web server with Visual Studio 2008 (Cassini).

I've set up a host on my local machine associating dev.testhost.com with 127.0.0.1, since I have an application that needs to change its appearance depending on the host header used to call it.

However, when I request my test application using http://dev.testhost.com:1234/index.aspx, the value of Request.Url.Host is always "localhost". Whereas the value of Request.Headers["host"] is "dev.testhost.com:1234" (as I would expect them both to be).

I'm NOT concerned that the second value includes the port number, but I am mighty confused as to why the HOST NAMES are completely different! Does anyone know if this is a known issue, or by design? Or am I being an idiot?!

I'd rather use Request.Url.Host, since that avoids having to strip out the port number when testing... - Removed due to possibly causing confusion! - Sam

A: 

It's a matter of what the w3 specs say versus what the Microsoft Uri.Host property is supposed to contain. The naming does not imply an attempt by MS to provide identical functionality. The function that does include port numbers is Uri.Authority.

With the update you posted, you're still facing the same problem, just examining a different aspect of it. The Uri.Host property is not explicity or implicity stated to perform the same function as the headers that are defined in the w3 specs. In long form, here are some quotes from the Uri.Host MSDN page:

Uri.Host Property
Gets the host component of this instance.

Property Value

Type: System.String

A String that contains the host name. This is usually the DNS host name or IP address of the server.

There's no guarantee that this will match what is in the headers, just that it represents the host name in some form.

jball
Thanks for your attempt, but you've mis-read the question, I've added emphasis now on the bit that said **I'm NOT concerned that the second value includes the port number**
Sam Salisbury
My answer is still applicable - I've expanded it to make the correlation to your update more obvious, but your basic problem is an assumption that a `System.Uri` object should directly match up with the http headers you're retrieving with `Request.Headers`.
jball
Objects of the `System.Uri` class just represent URIs in general, my question is about the `Request.Url` **instance** of the `System.Uri` class, as accessed from an aspx page that is being rendered due to a request for a particular URL. What is the point of the property `Request.Uri` if it does not match the `Request`'s Url?To clarify, you can call 'Request.Url.ToString()' and it still reports `http://localhost:1234/Default.aspx` whereas the request was for `http://dev.testhost.com:1234/Default.aspx`...
Sam Salisbury
Oops, `Request.Uri` should have been `Request.Url`...
Sam Salisbury
Simply put, `Request.Url` is by definition processed, not raw. The responses found in `Request.Headers` are not. A processed `Url.Host` should not be expected to match the raw output obtained from `Request.Headers["host"]` but should be expected to represent the same URI. This may be surprising to you, but it is not an issue.
jball
But http://localhost:1234/ is not the same URI as http://dev.testhost.com:1234/ they are different as you can see ;)
Sam Salisbury
In the case of your machine they are the same, as they identify the same resource. Hence the name, Universal Resource Identifier/Locator.
jball
I've been digging into this more, and I can't find any standards on what makes URIs equivalent. I see your point about knowing what the client sent, but I'm still not sure that Cassini or `Request.Url` returning a different hostname than what you expected is questionable, or if the fact that they are equivalent on your machine means that it is completely acceptable.
jball
+6  A: 

Request.Headers["host"] is the value received from the application that connects to the server, while the other value is the one the server gets when it tries to get the domain name.

The browser uses in the request the domain name entered because that is used in the case of virtual domains. The server reports the one set in the server preferences, or the first one it finds.

EDIT: Looking at the code of Cassini to see if it uses some particular settings, I noticed the following code:

public string RootUrl {
  get {
    if (_port != 80) {
      return "http://localhost:" + _port + _virtualPath;
    }
    else {
      return "http://localhost" + _virtualPath;
    }
  }
}

//
// Socket listening
//

public void Start() {
  try {
    _socket = CreateSocketBindAndListen(AddressFamily.InterNetwork, IPAddress.Loopback, _port);
  }
  catch {
    _socket = CreateSocketBindAndListen(AddressFamily.InterNetworkV6, IPAddress.IPv6Loopback, _port);
  }
  // …
}

The explanation seems to be that Cassini makes explicit reference to localhost, and doesn't try to make a reverse DNS lookup. Differently, it would not have code as return "http://localhost" + _virtualPath;.

kiamlaluno
Well, the IP address of the server is 127.0.0.1. This has 2 hosts mapped in c:\windows\system32\drivers\etc\hosts: **localhost** and **dev.testhost.com** However, when I type in 'dev.testhost.com' the value of `HttpContect.Current.Request.Url.Host` is 'localhost'? On a production server, it will be whatever I type in. Why is the local testing server (Cassini) different from the live server?
Sam Salisbury
In which order are the hosts defined? Probably the order influences the one reported by the server.
kiamlaluno
I just tried changing the order of the hosts and this made no difference! Good idea though ;) I think it would be surprising if this worked, since that would probably require a reverse DNS lookup if it was to work consistently, which I doubt they'd put in a simple-sounding property... I suspect there is a bug in Cassini which causes this discrepancy!
Sam Salisbury
Are you sure there aren't any settings to change which domain name is returned from the server?
kiamlaluno
Really, I'm just confused as to why the **property** `Url` of the `HttpRequest` class doesn't match the URL of the HTTP request which the `HttpRequest` object is meant to represent... If it does some kind of server interrogation to discover some other host name associated with the server, I would expect it to be a property of some other object rather than a `HttpRequest` instance...
Sam Salisbury
Thank you! This makes perfect sense then, why the behaviour is different on Cassini than IIS :) This is the accepted answer. Could you give me a vote, as I think this justifies the question as legitimate... Thanks again!
Sam Salisbury
+6  A: 

The Request.Headers["host"] is the host as specified in the http header from the browser. (e.g. this is what you'd see if you examined the traffic with Fiddler or HttpWatch)

However, ASP.NET loasds this (and other request info) into a System.Uri instance, which parses the request string into its constituent parts. In this case, "Host" refers to literally the host machine part of the original request (e.g. with the tcp port being in the Port) property.

This System.Uri class is a very useful helper class that takes all the pain out of splitting your request into it's parts, whereas the "Host:" (and for that matter the "GET") from the http header are just raw request data.

Although they both have the same name, they are not meant to be the same thing.

Rob Levine
Thanks for your answer, what I'm wondering though is how 'splitting up' http://dev.testhost.com:1234/ can result in completely losing the 'dev.testhost.com' part, and have it replaced with 'localhost'? My question wasn't really about the Uri or UriBuilder classes, I understand those well ;) Also, **the port number isn't the issue here at all**. I've added **emphasis** to the 3rd paragraph of my question, maybe that will make it clearer? Thanks again ;)
Sam Salisbury
Well - you've said you have associated dev.testhost.com with the IP address 127.0.0.1. However, this IP address always maps to the localhost (by definition - it is the loopback address) - so any name lookup on 127.0.0.1 will map to localhost.When you say you "associating dev.testhost.com with 127.0.0.1" how did you do this? Did you edit the hosts file, or do it some other way?
Rob Levine
I added the entry in the hosts file. Please see kiamlaluno's answer below... It looks like this is a Cassini-specific issue...
Sam Salisbury