Skip to content
This repository has been archived by the owner on Jun 10, 2020. It is now read-only.

Requests with the url http:/// causes the exception #278

Closed
SergeyKanzhelev opened this issue Oct 25, 2016 · 22 comments
Closed

Requests with the url http:/// causes the exception #278

SergeyKanzhelev opened this issue Oct 25, 2016 · 22 comments
Labels
Milestone

Comments

@SergeyKanzhelev
Copy link
Contributor

Reported by @stuartleeks: Trying to extract the host address from the url 'http:///' causes the exception.

@stuartleeks
Copy link

Thanks @SergeyKanzhelev

I have packaged an ASP.NET Core application in a Docker image and use the Application Insights .NET Core SDK. When I run the container under Marathon (on DCOS), the healthcheck URI sometimes shows in the logs as http:///. Unfortunately App Insights request pipeline throws an exception trying to retrieve the host name for these requests.

@stuartleeks
Copy link

stuartleeks commented Oct 25, 2016

I thought I'd saved the exception details, but can't find it. I'll recreate it if I get chance, but this is the line that was throwing IIRC: https://github.com/Microsoft/ApplicationInsights-aspnetcore/blob/bde2dcaf60ccbaf9b759597d9482ef4efa774fae/src/Microsoft.ApplicationInsights.AspNetCore/Extensions/HttpRequestExtensions.cs#L31

@SergeyKanzhelev
Copy link
Contributor Author

Do you only get http:/// or also something like http:///items/10? I wonder how it is possible to get an empty host name.

@stuartleeks
Copy link

stuartleeks commented Oct 26, 2016

I set up the healthcheck in marathon to hit `/?healthcheck' (i.e. the root of the site with a healthcheck query string parameter to make the health checks easy to identify.

In the logs I see http://10.32.0.5:11800/?healthcheck and http:///?healthcheck, and the latter of these causes an exception:

fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HKVTKBO51EQE": An unhandled exception was thrown by the application.
System.ArgumentException: Http request Host is not specified
   at Microsoft.ApplicationInsights.AspNetCore.Extensions.HttpRequestExtensions.GetUri(HttpRequest request)
   at Microsoft.ApplicationInsights.AspNetCore.RequestTrackingMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.<Invoke>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.<RequestProcessingAsync>d__2.MoveNext()

If I disable Application Insights and redeploy then the app runs fine (i.e. ASP.NET Core is happy with the empty hostname):

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.0 GET http:///?healthcheck
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method Chess.Web.Controllers.GameController.Home (Chess.Web) with arguments () - ModelState is Valid
info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1]
      Executing ViewResult, running view at path /Views/Game/Home.cshtml.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action Chess.Web.Controllers.GameController.Home (Chess.Web) in 1.1041ms
...

@SergeyKanzhelev
Copy link
Contributor Author

@Eilon, have you ever seen the empty hostname in URL? BTW, is there a way to get RawUrl from the request instead of constructing it like this: https://github.com/Microsoft/ApplicationInsights-aspnetcore/blob/master/src/Microsoft.ApplicationInsights.AspNetCore/Extensions/HttpRequestExtensions.cs#L17?

@Eilon
Copy link
Member

Eilon commented Oct 26, 2016

@Tratcher would know best. This does seem very odd.

@stuartleeks
Copy link

With System.Uri, if I try new Uri("http:///") then I get

Unhandled Exception: System.UriFormatException: Invalid URI: The hostname could not be parsed.
   at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind)
   at System.Uri..ctor(String uriString)

@Tratcher
Copy link
Member

Can you provide a full trace of the request? E.g. Fiddler/wireshark/etc.?

HTTP/1.0 did not require a Host header, that was a new requirement in HTTP/1.1. The client had a host in their original URL, but they did not transmit it to the server. Kestrel does not require a Host header on any version.

You can use GetEncodedUrl https://github.com/aspnet/HttpAbstractions/blob/dev/src/Microsoft.AspNetCore.Http.Extensions/UriHelper.cs#L166 to build the url correctly for most scenarios. Your version leaves out PathBase and incorrectly encodes Path https://github.com/Microsoft/ApplicationInsights-aspnetcore/blob/master/src/Microsoft.ApplicationInsights.AspNetCore/Extensions/HttpRequestExtensions.cs#L42. However GetEncodedUrl doesn't handle the missing Host scenario any better. When the Host is missing you must either provide a default or generate a relative uri (e.g. only the path and query).

The RawUrl is a misconception, there is no full raw url transmitted in an HTTP request, only pieces that must be assembled.

@stuartleeks
Copy link

I can't easily get the trace - I'm running this in Container Service under DCOS and Marathon. Marathon is executing the healthchecks, and the http:/// is coming from the default ASP.NET Core logging as per my earlier comment.

The only reason I tested parsing http:/// is because that is what the App Insights code here would do if the hostname check were relaxed: https://github.com/stuartleeks/ApplicationInsights-aspnetcore/blob/master/src/Microsoft.ApplicationInsights.AspNetCore/Extensions/HttpRequestExtensions.cs#L36

They're working with HttpRequest and seeing request.Host.HasValue as false.

In the absence of fiddler capture, is there any extra logging I can enable for ASP.NET Core that would help?

@Tratcher
Copy link
Member

You can also enable Kestrel's connection level logging:
https://github.com/aspnet/KestrelHttpServer/blob/dev/samples/SampleApp/Startup.cs#L42

@stuartleeks
Copy link

stuartleeks commented Oct 27, 2016

Ok, I turned on the Kestrel logging and got the following output for the request without a hostname:

�[100m�[37mdbug�[39m�[0m:` LoggingConnectionFilter[0]
      ReadAsync[30] 47 45 54 20 2F 3F 68 65 61 6C 74 68 63 68 65 63 6B 20 48 54 54 50 2F 31 2E 30 0D 0A 0D 0A 
      GET /?healthcheck HTTP/1.0




�[100m�[32minfo�[39m�[0m: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.0 GET http:///?healthcheck  

I do also see requests with the hostname:

�[100m�[37mdbug�[39m�[0m: LoggingConnectionFilter[0]
      ReadAsync[81] 47 45 54 20 2F 3F 68 65 61 6C 74 68 63 68 65 63 6B 20 48 54 54 50 2F 31 2E 31 0D 0A 48 6F 73 74 3A 20 31 30 2E 33 32 2E 30 2E 35 3A 31 35 31 30 0D 0A 55 73 65 72 2D 41 67 65 6E 74 3A 20 73 70 72 61 79 2D 63 61 6E 2F 31 2E 33 2E 33 0D 0A 0D 0A 
      GET /?healthcheck HTTP/1.1

      Host: 10.32.0.5:1510

      User-Agent: spray-can/1.3.3




�[100m�[32minfo�[39m�[0m: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://10.32.0.5:1510/?healthcheck  

@Tratcher
Copy link
Member

Ok, so it is a 1.0 request with no host header. @SergeyKanzhelev it looks like that method is only used here: https://github.com/Microsoft/ApplicationInsights-aspnetcore/blob/56dc5297134406d6b028076aa5b3582311412e74/src/Microsoft.ApplicationInsights.AspNetCore/RequestTrackingMiddleware.cs#L56 for logging purposes. In this case throwing is likely overkill, you should be able to provide defaults for any unknown values. e.g. "http://unspecified/path?query"

@Dmitry-Matveev Dmitry-Matveev added this to the Dec-16 milestone Nov 18, 2016
@DamianEdwards
Copy link
Member

@rh78
Copy link

rh78 commented Mar 14, 2017

Same problem here - I see it in the logs but do not know if there is any real problem coming out of it (will it throw to user level?)

@stuartleeks
Copy link

The issue I hit was that the healthcheck in DC/OS made the request, got a bad response and decided that the container instance was unhealthy and tore it down

@rh78
Copy link

rh78 commented Mar 14, 2017

I see in Application Insights Log (running on Azure, ASP.NET Core on 4.6.2 FULL .Net Framework):
(it occurs every few minutes at random intervals):

System.ArgumentException:
   at Microsoft.ApplicationInsights.AspNetCore.Extensions.HttpRequestExtensions.GetUri (Microsoft.ApplicationInsights.AspNetCore, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35)
   at Microsoft.ApplicationInsights.AspNetCore.DiagnosticListeners.HostingDiagnosticListener.EndRequest (Microsoft.ApplicationInsights.AspNetCore, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35)
   at Proxy_Method_From_<>f__AnonymousType0`2_To_Void OnEndRequest(Microsoft.AspNetCore.Http.HttpContext, Int64) (Anonymously Hosted DynamicMethods Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
   at Microsoft.Extensions.DiagnosticAdapter.DiagnosticSourceAdapter.Write (Microsoft.Extensions.DiagnosticAdapter, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.Extensions.DiagnosticAdapter.DiagnosticSourceAdapter.System.IObserver<System.Collections.Generic.KeyValuePair<System.String,System.Object>>.OnNext (Microsoft.Extensions.DiagnosticAdapter, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at System.Diagnostics.DiagnosticListener.Write (System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51)
   at Microsoft.AspNetCore.Hosting.Internal.HostingApplication.DisposeContext (Microsoft.AspNetCore.Hosting, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.AspNetCore.Server.WebListener.MessagePump+ApplicationWrapper`1.DisposeContext (Microsoft.AspNetCore.Server.WebListener, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.AspNetCore.Server.WebListener.MessagePump+<ProcessRequestAsync>d__23.MoveNext (Microsoft.AspNetCore.Server.WebListener, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.AspNetCore.Server.WebListener.MessagePump+<ProcessRequestAsync>d__23.MoveNext (Microsoft.AspNetCore.Server.WebListener, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)

@trondhindenes
Copy link

Any update on this? A lot of container schedulers seem to do healthchecks without sending in a host header.

@glennc
Copy link

glennc commented Jan 23, 2018

If I read all this correctly then the following is true:

  1. If there is no host header then the GetUri extension method will throw and fail the request
  2. In HTTP 1.0 host is optional
  3. Some health check systems, like DC/OS or HAProxy, send a 1.0 request by default with no host header.

There are two main workarounds:

  1. Configure them to send the header. I know HAProxy can be configured for this, can the DC/OS system?
  2. Put a proxy in front to normalize requests and handle requests with no header.

From an ASP.NET perspective, we could add a configuration option that lets you specify a string to use in place of the host when one isn't required. That would just add another option to the workarounds. But presumably we should be having GetUri not throw. So that leads to this question:

@SergeyKanzhelev presumably we need a valid URL for RequestTelemetry.URL. But is it used for more than just information to the user? App Insights could possibly leave it out or do something like use UNKNOWN_HOST if, for the known case of telemetry.Url, that is OK. But I don't know that it is.

@SergeyKanzhelev
Copy link
Contributor Author

Application Insights does not do a lot of parsing of URL on backend. But it is enforces in C#. UNKNOWN_HOST will be good enough

@stuartleeks
Copy link

Looking at the code, AppInsights checks whether the host is set here, but that method returns a Uri instance. The result is consumed here and assigned to RequestTelemetry.Url which is a System.Uri and the Uri contstructor doesn't access UNKNOWN_HOST as a value. Any suggestions for an alternative to UNKNOWN_HOST?

@Tratcher
Copy link
Member

UNKNOWN-HOST

@cijothomas
Copy link
Contributor

Fixed with #677

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

10 participants