I am running an azure function app with runtime 4, .NET 6.0.
When I call the function app in a locally running environment the req.HttpContext.GetServerVariable() always returns null because the IServerVariablesFeature is not supported in the list of context.Features.
When I deploy this to an azure hosted instance of the function app, the variables are populated correctly. I have done extensive research and have been unable to find out if this is intentionally not supported or if I am missing some form of additional configuration in my local environment.
Here is a test function that attempts to read 3 different server variables and returns them as a string and can be called using a get request in postman or via a browser http://localhost:7071/api/GetServerVariable:
public static class TestFunction
{
[FunctionName("GetServerVariable")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "GetServerVariable")] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var url = req.HttpContext.GetServerVariable("URL");
var remoteAddr = req.HttpContext.GetServerVariable("REMOTE_ADDR");
var https = req.HttpContext.GetServerVariable("HTTPS");
var response = $"Current server variables: URL: {url} - REMOTE_ADDR: {remoteAddr} - HTTPS: {https}";
log.LogWarning(response);
return new OkObjectResult(response);
}
}
According to this document, GetServerVariable returns
null if the server does not support the IServerVariablesFeature feature. May return null or empty if the variable does not exist or is not set.
One of the workaround is to include Forwarded headers Middleware:
Before the request reaches the app, proxy servers, load balancers, and other network appliances usually hide information about the request. The original scheme is lost when HTTPS requests are proxied via HTTP and must be transmitted in a header. The originating client IP address must also be forwarded in a header because an app receives a request from the proxy rather than its true source on the Internet or corporate network.
From the MSDN article:
Although retrieving just the REMOTE_ADDR server variable should be enough, I found resources online that suggested that code like this should also check the HTTP_X_FORWARDED_FOR variable; if the request comes through a proxy server that translates the address, it's this variable that contains the correct address. If you request a server variable that doesn't exist, the ServerVariables property returns an empty string. Therefore, even though this property doesn't appear in my tests, attempting to retrieve its value doesn't cause trouble.
The Forwarded Headers Middleware reads the headers X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Proto and fills in the associated fields on HttpContext.
REFERENCES:- How to access server variables in ASP.Net Core
Related
Basically, I need to be able to make an HTTP Request to a Website on the same machine I am on, without modifying the host file to create a pointer to the domain name.
For example.
I am running the code on one website, let's say www.bobsoft.com which is on a server.
I need to make an HTTP request to www.tedsoft.com which is on the same server.
How can I make a call using a C# HttpClient without modifying the host file? Take into account that the websites are routed by bindings in IIS. I do know the domain I am going to use ahead of time, I just have to make it all internal in the code without server changes.
Thanks!
IIS bindings on the same port but different hostnames are routed based on the http Host header. The best solution here is really to configure local DNS so requests made to www.tedsoft.com don't leave the machine. That being said, if these kinds of configuration aren't an option you can easily set the host header as a part of your HttpRequestMessage.
I have 2 test sites configured on IIS.
Default Web Site - returns text "test1"
Default Web Site 2 - returns text "test2"
The following code uses http://127.0.0.1 (http://localhost also works) and sets the host header appropriately based on the IIS bindings to get the result you're looking for.
class Program
{
static HttpClient httpClient = new HttpClient();
static void Main(string[] args)
{
string test1 = GetContentFromHost("test1"); // gets content from Default Web Site - "test1"
string test2 = GetContentFromHost("test2"); // gets content from Default Web Site 2 - "test2"
}
static string GetContentFromHost(string host)
{
HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Get, "http://127.0.0.1");
msg.Headers.Add("Host", host);
return httpClient.SendAsync(msg).Result.Content.ReadAsStringAsync().Result;
}
}
Using Visual Studio 2017, I created a Function App with a Generic WebHook:
public static class FunctionWebHook
{
[FunctionName("FunctionWebHook")]
public static async Task<object> Run([HttpTrigger(WebHookType = "genericJson")]HttpRequestMessage request, TraceWriter log)
{
log.Info($"Webhook was triggered!");
string jsonContent = await request.Content.ReadAsStringAsync();
log.Info(jsonContent);
return request.CreateResponse(HttpStatusCode.NoContent);
}
}
The code is little more than the default template. I deployed this to my Azure account and tried to invoke it. I used the 'Get function url' link on the portal to get the correct URL, this included both the code and clientId parameters. When I try to POST JSON to the function (with content type set to application/json) I receive a 400 Bad Request:
{"Message":"The 'code' query parameter provided in the HTTP request did not match the expected value."}
I've check the code parameter and it is correct. I've also re-created the Function App several times, however I continue to receive the error. When I invoke the function using the Portal's Run command it executes correctly.
Has anyone come across this issue before?
So I use Restlet Client for any API work and it seems it has a strange issue. I copied the default (Host Key) from the Portal and pasted the URL into the Restlet Client and for some reason the last '==' of the code parameter is dropped. I tried the request using Postman and that request worked!
Thanks for all of the comments and the reply!
Which key did you choose to authenticate your request? There are 3 types of keys. Please choose the default(Function key) and use the generated URL and the key. I tested it on my side and the function key could pass the validation from server.
For more information of function key and host key, link below is for your reference.
Azure Function WebHook API Keys
I need to create an AuthroizationFilter for my Hangfire Dashboard.
Its running on an Azure VM and by design should only accept request from local requests.
I want to create an AuthorizationFilter that validates only those requests from the web browser on the same VM as the web app is running on.
I need to determine this form the OwinContext :-
public class MyRestrictiveAuthorizationFilter : IAuthorizationFilter
{
public bool Authorize(IDictionary<string, object> owinEnvironment)
{
// In case you need an OWIN context, use the next line,
// `OwinContext` class is the part of the `Microsoft.Owin` package.
var context = new OwinContext(owinEnvironment);
// Allow all local request to see the Dashboard
return true;
}
}
I had a similar issue working on a custom CookieAuthenticationProvider where only IOwinContext was exposed. context.Request did not expose an IsLocal property, however the following was available:
context.Request.Uri.IsLoopback
The property was true for 127.0.0.1 and localhost requests.
What about checking the headers in the request context?
context.Request.Headers["Referer"]
If the value includes localhost or 127.0.0.1 or whatever you're restricting it to.
CAUTION: this header can be spoofed. There may be a better way to limit access to the dashboard. Does it need internet access?
Can you use HttpRequest.IsLocal? This will tell you if the IP address of the request is the same as the server's IP address.
return context.Request.IsLocal;
We use Request.Url.GetLeftPart(UriPartial.Authority) to get the domain part of the site. This served our requirement on http.
We recently change site to https (about 3 days ago) but this still returns with http://..
Urls were all changed to https and show in browser address bar.
Any idea why this happens?
The following example works fine and returns a string with "https":
var uri = new Uri("https://www.google.com/?q=102njgn24gk24ng2k");
var authority = uri.GetLeftPart(UriPartial.Authority);
// authority => "https://www.google.com"
You either have an issue with the HttpContext class right here, or all your requests are still using http:
You can check the requests HttpContext.Current.Request.IsSecureConnection property. If it is true, and the GetLeftPart method still returns http for you, I think you won't get around a replacing here.
If all your requests are really coming with http, you might enforce a secure connection in IIS.
You should also inspect the incoming URL and log it somewhere for debugging purposes.
This can also happen when dealing with a load balancer. In one situation I worked on, any https requests were converted into http by the load balancer. It still says https in the browser address bar, but internally it's a http request, so the server-side call you are making to GetLeftPart() returns http.
If your request is coming from ARR with SSL Offloading,
Request.Url.GetLeftPart(UriPartial.Authority) just get http
I am using a WCF service client generated by slsvcutil form Silverlight toolkit version 4. I've also tried version 3 with the same problems. When I use a client instance running on http with no user credentials it runs without problems. But I need to switch to https for productive servers and send user credentials that are hardcoded for my application. I use the following code for that:
var binding = new BasicHttpBinding (BasicHttpSecurityMode.TransportCredentialOnly);
var endpoint = new EndpointAddress (AppSettings.FlareEndPoint);
_service = new TopicAnalystAPIClient(binding, endpoint);
_service.ClientCredentials.UserName.UserName = "xxx";
_service.ClientCredentials.UserName.Password = "xxx";
When I call a method on that service pointing to http with no authentication it works. When I use the this code against http/https with the credential I get "There was an error on processing web request: Status code 401(Unauthorized): Unauthorized" exception. I've checked that the credentials are correct, I am able to open the service reference in my browser. I've also tried several combinations of http/https and SecurityMode value. I've also tried it on four different servers always with the same result.
What can be the problem?
A lot of permutations are possible. BasicHttpSecurityMode.TransportCredentialOnly should be usable without SSL [1] using HTTP itself. This means the server will send one (or more) authentication method(s) to the client (e.g. basic, digest, ntlm) and Mono (including MonoTouch) should be providing support for the most of them.
It is possible that the linker (if used) removes one of them. In that case you could try building and testing without linking (or skip linking of System.Net.dll).
It's also possible that the authentication method that the serve insist on is not supported. You could find which one is used by running a network trace (e.g. wireshark) or, maybe, it will show up in more details in the server log (along with the 401 error).
[1] http://msdn.microsoft.com/en-us/library/system.servicemodel.basichttpsecuritymode%28v=vs.95%29.aspx