internal async Task GeneratedMethods_WithOutParams_DoNotCauseDeadlocks(string fixture) { var traceWriter = new TestTraceWriter(TraceLevel.Verbose); ScriptHostConfiguration config = new ScriptHostConfiguration() { RootScriptPath = @"TestScripts\FunctionGeneration", TraceWriter = traceWriter }; string secretsPath = Path.Combine(Path.GetTempPath(), @"FunctionTests\Secrets"); ISecretsRepository repository = new FileSystemSecretsRepository(secretsPath); WebHostSettings webHostSettings = new WebHostSettings(); webHostSettings.SecretsPath = secretsPath; var secretManager = new SecretManager(SettingsManager, repository, NullTraceWriter.Instance); using (var manager = new WebScriptHostManager(config, new TestSecretManagerFactory(secretManager), SettingsManager, webHostSettings)) { Thread runLoopThread = new Thread(_ => { manager.RunAndBlock(CancellationToken.None); }); runLoopThread.IsBackground = true; runLoopThread.Start(); await TestHelpers.Await(() => { return(manager.State == ScriptHostState.Running); }); var request = new HttpRequestMessage(HttpMethod.Get, string.Format("http://localhost/api/httptrigger-{0}", fixture)); FunctionDescriptor function = manager.GetHttpFunctionOrNull(request); SynchronizationContext currentContext = SynchronizationContext.Current; var resetEvent = new ManualResetEventSlim(); try { var requestThread = new Thread(() => { var context = new SingleThreadedSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(context); manager.HandleRequestAsync(function, request, CancellationToken.None) .ContinueWith(task => resetEvent.Set()); Thread.Sleep(500); context.Run(); }); requestThread.IsBackground = true; requestThread.Start(); bool threadSignaled = resetEvent.Wait(TimeSpan.FromSeconds(10)); requestThread.Abort(); Assert.True(threadSignaled, "Thread execution did not complete"); } finally { SynchronizationContext.SetSynchronizationContext(currentContext); manager.Stop(); } } }
public override async Task <HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken) { HttpRequestMessage request = controllerContext.Request; // First see if the request maps to an HTTP function FunctionDescriptor function = _scriptHostManager.GetHttpFunctionOrNull(request.RequestUri); if (function == null) { return(new HttpResponseMessage(HttpStatusCode.NotFound)); } // Determine the authorization level of the request SecretManager secretManager = (SecretManager)controllerContext.Configuration.DependencyResolver.GetService(typeof(SecretManager)); AuthorizationLevel authorizationLevel = AuthorizationLevelAttribute.GetAuthorizationLevel(request, secretManager, functionName: function.Name); if (function.Metadata.IsDisabled && authorizationLevel != AuthorizationLevel.Admin) { // disabled functions are not publically addressable w/o Admin level auth return(new HttpResponseMessage(HttpStatusCode.NotFound)); } // Dispatch the request HttpTriggerBindingMetadata httpFunctionMetadata = (HttpTriggerBindingMetadata)function.Metadata.InputBindings.FirstOrDefault(p => p.Type == BindingType.HttpTrigger); bool isWebHook = !string.IsNullOrEmpty(httpFunctionMetadata.WebHookType); HttpResponseMessage response = null; if (isWebHook) { if (authorizationLevel == AuthorizationLevel.Admin) { // Admin level requests bypass the WebHook auth pipeline response = await _scriptHostManager.HandleRequestAsync(function, request, cancellationToken); } else { // This is a WebHook request so define a delegate for the user function. // The WebHook Receiver pipeline will first validate the request fully // then invoke this callback. Func <HttpRequestMessage, Task <HttpResponseMessage> > invokeFunction = async(req) => { // Reset the content stream before passing the request down to the function Stream stream = await req.Content.ReadAsStreamAsync(); stream.Seek(0, SeekOrigin.Begin); return(await _scriptHostManager.HandleRequestAsync(function, req, cancellationToken)); }; response = await _webHookReceiverManager.HandleRequestAsync(function, request, invokeFunction); } } else { // Authorize if (authorizationLevel < httpFunctionMetadata.AuthLevel) { return(new HttpResponseMessage(HttpStatusCode.Unauthorized)); } // Validate the HttpMethod // Note that for WebHook requests, WebHook receiver does its own validation if (httpFunctionMetadata.Methods != null && !httpFunctionMetadata.Methods.Contains(request.Method)) { return(new HttpResponseMessage(HttpStatusCode.MethodNotAllowed)); } // Not a WebHook request so dispatch directly response = await _scriptHostManager.HandleRequestAsync(function, request, cancellationToken); } return(response); }
public override async Task <HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken) { HttpRequestMessage request = controllerContext.Request; // First see if the request maps to an HTTP function FunctionDescriptor function = _scriptHostManager.GetHttpFunctionOrNull(request); if (function == null) { return(new HttpResponseMessage(HttpStatusCode.NotFound)); } // Determine the authorization level of the request ISecretManager secretManager = controllerContext.Configuration.DependencyResolver.GetService <ISecretManager>(); var settings = controllerContext.Configuration.DependencyResolver.GetService <WebHostSettings>(); var authorizationLevel = settings.IsAuthDisabled ? AuthorizationLevel.Admin : await AuthorizationLevelAttribute.GetAuthorizationLevelAsync(request, secretManager, functionName : function.Name); if (function.Metadata.IsExcluded || (function.Metadata.IsDisabled && authorizationLevel != AuthorizationLevel.Admin)) { // disabled functions are not publicly addressable w/o Admin level auth, // and excluded functions are also ignored here (though the check above will // already exclude them) return(new HttpResponseMessage(HttpStatusCode.NotFound)); } // Dispatch the request HttpTriggerBindingMetadata httpFunctionMetadata = (HttpTriggerBindingMetadata)function.Metadata.InputBindings.FirstOrDefault(p => string.Compare("HttpTrigger", p.Type, StringComparison.OrdinalIgnoreCase) == 0); bool isWebHook = !string.IsNullOrEmpty(httpFunctionMetadata.WebHookType); HttpResponseMessage response = null; if (isWebHook) { if (authorizationLevel == AuthorizationLevel.Admin) { // Admin level requests bypass the WebHook auth pipeline response = await _scriptHostManager.HandleRequestAsync(function, request, cancellationToken); } else { // This is a WebHook request so define a delegate for the user function. // The WebHook Receiver pipeline will first validate the request fully // then invoke this callback. Func <HttpRequestMessage, Task <HttpResponseMessage> > invokeFunction = async(req) => { // Reset the content stream before passing the request down to the function Stream stream = await req.Content.ReadAsStreamAsync(); stream.Seek(0, SeekOrigin.Begin); return(await _scriptHostManager.HandleRequestAsync(function, req, cancellationToken)); }; response = await _webHookReceiverManager.HandleRequestAsync(function, request, invokeFunction); } } else { // Authorize if (authorizationLevel < httpFunctionMetadata.AuthLevel) { return(new HttpResponseMessage(HttpStatusCode.Unauthorized)); } // Not a WebHook request so dispatch directly response = await _scriptHostManager.HandleRequestAsync(function, request, cancellationToken); } return(response); }