Beispiel #1
0
        internal async Task <HttpResponseMessage> ProcessAsync(HttpRequestMessage req, string functionName, Func <JArray, string, CancellationToken, Task <HttpResponseMessage> > eventsFunc, CancellationToken cancellationToken)
        {
            IEnumerable <string> eventTypeHeaders = null;
            string eventTypeHeader = null;

            if (req.Headers.TryGetValues("aeg-event-type", out eventTypeHeaders))
            {
                eventTypeHeader = eventTypeHeaders.First();
            }

            if (String.Equals(eventTypeHeader, "SubscriptionValidation", StringComparison.OrdinalIgnoreCase))
            {
                string jsonArray = await req.Content.ReadAsStringAsync().ConfigureAwait(false);

                SubscriptionValidationEvent validationEvent = null;
                List <JObject> events = JsonConvert.DeserializeObject <List <JObject> >(jsonArray);
                // TODO remove unnecessary serialization
                validationEvent = ((JObject)events[0]["data"]).ToObject <SubscriptionValidationEvent>();
                SubscriptionValidationResponse validationResponse = new SubscriptionValidationResponse {
                    ValidationResponse = validationEvent.ValidationCode
                };
                var returnMessage = new HttpResponseMessage(HttpStatusCode.OK);
                returnMessage.Content = new StringContent(JsonConvert.SerializeObject(validationResponse));
                _logger.LogInformation($"perform handshake with eventGrid for function: {functionName}");
                return(returnMessage);
            }
            else if (String.Equals(eventTypeHeader, "Notification", StringComparison.OrdinalIgnoreCase))
            {
                JArray events         = null;
                string requestContent = await req.Content.ReadAsStringAsync().ConfigureAwait(false);

                var token = JToken.Parse(requestContent);
                if (token.Type == JTokenType.Array)
                {
                    // eventgrid schema
                    events = (JArray)token;
                }
                else if (token.Type == JTokenType.Object)
                {
                    // cloudevent schema
                    events = new JArray
                    {
                        token
                    };
                }

                return(await eventsFunc(events, functionName, cancellationToken).ConfigureAwait(false));
            }
            else if (String.Equals(eventTypeHeader, "Unsubscribe", StringComparison.OrdinalIgnoreCase))
            {
                // TODO disable function?
                return(new HttpResponseMessage(HttpStatusCode.Accepted));
            }

            return(new HttpResponseMessage(HttpStatusCode.BadRequest));
        }
        private async Task <HttpResponseMessage> ProcessAsync(HttpRequestMessage req)
        {
            // webjobs.script uses req.GetQueryNameValuePairs();
            // which requires webapi.core...but this does not work for .netframework2.0
            // TODO change this once webjobs.script is migrated
            var functionName = HttpUtility.ParseQueryString(req.RequestUri.Query)["functionName"];

            if (String.IsNullOrEmpty(functionName) || !_listeners.ContainsKey(functionName))
            {
                _tracer.Trace(new TraceEvent(System.Diagnostics.TraceLevel.Info,
                                             $"cannot find function: '{functionName}', available function names: [{string.Join(", ", _listeners.Keys.ToArray())}]"));
                return(new HttpResponseMessage(HttpStatusCode.NotFound));
            }

            IEnumerable <string> eventTypeHeaders = null;
            string eventTypeHeader = null;

            if (req.Headers.TryGetValues("aeg-event-type", out eventTypeHeaders))
            {
                eventTypeHeader = eventTypeHeaders.First();
            }

            if (String.Equals(eventTypeHeader, "SubscriptionValidation", StringComparison.OrdinalIgnoreCase))
            {
                string jsonArray = await req.Content.ReadAsStringAsync();

                SubscriptionValidationEvent validationEvent = null;
                List <JObject> events = JsonConvert.DeserializeObject <List <JObject> >(jsonArray);
                // TODO remove unnecessary serialization
                validationEvent = ((JObject)events[0]["data"]).ToObject <SubscriptionValidationEvent>();
                SubscriptionValidationResponse validationResponse = new SubscriptionValidationResponse {
                    ValidationResponse = validationEvent.ValidationCode
                };
                var returnMessage = new HttpResponseMessage(HttpStatusCode.OK);
                returnMessage.Content = new StringContent(JsonConvert.SerializeObject(validationResponse));
                _tracer.Trace(new TraceEvent(System.Diagnostics.TraceLevel.Info,
                                             $"perform handshake with eventGrid for endpoint: {req.RequestUri}"));
                return(returnMessage);
            }
            else if (String.Equals(eventTypeHeader, "Notification", StringComparison.OrdinalIgnoreCase))
            {
                string jsonArray = await req.Content.ReadAsStringAsync();

                List <JObject> events = JsonConvert.DeserializeObject <List <JObject> >(jsonArray);

                foreach (var ev in events)
                {
                    TriggeredFunctionData triggerData = new TriggeredFunctionData
                    {
                        TriggerValue = ev
                    };

                    await _listeners[functionName].Executor.TryExecuteAsync(triggerData, CancellationToken.None);
                }

                return(new HttpResponseMessage(HttpStatusCode.Accepted));
            }
            else if (String.Equals(eventTypeHeader, "Unsubscribe", StringComparison.OrdinalIgnoreCase))
            {
                // TODO disable function?
                return(new HttpResponseMessage(HttpStatusCode.Accepted));
            }

            return(new HttpResponseMessage(HttpStatusCode.BadRequest));
        }
        internal async Task <HttpResponseMessage> ProcessAsync(
            HttpRequestMessage req,
            string functionName,
            Func <JArray, string, CancellationToken, Task <HttpResponseMessage> > eventsFunc,
            CancellationToken cancellationToken)
        {
            string eventTypeHeader = null;

            if (req.Headers.TryGetValues(EventTypeKey, out IEnumerable <string> eventTypeHeaders))
            {
                eventTypeHeader = eventTypeHeaders.First();
            }

            // Subscription validation handshake
            if (string.Equals(eventTypeHeader, SubscriptionValidationEvent, StringComparison.OrdinalIgnoreCase))
            {
                string validationCode;
                string json = await req.Content.ReadAsStringAsync().ConfigureAwait(false);

                JToken events = JToken.Parse(json);

                switch (events.Type)
                {
                case JTokenType.Array:
                    validationCode = events[0][DataKey][ValidationCodeKey].ToString();
                    break;

                case JTokenType.Object:
                {
                    // The Data is double-encoded in the CloudEvent subscription event
                    var data = JToken.Parse(events[DataKey].ToString());
                    validationCode = data[ValidationCodeKey].ToString();
                    break;
                }

                default:
                    throw new ArgumentOutOfRangeException(
                              $"The request content should be parseable into a JSON object or array, but was {events.Type}.");
                }

                SubscriptionValidationResponse validationResponse = new SubscriptionValidationResponse {
                    ValidationResponse = validationCode
                };
                var returnMessage = new HttpResponseMessage(HttpStatusCode.OK)
                {
                    // use System.Text.Json to leverage the custom converter so that the casing is correct.
                    Content = new StringContent(STJ.JsonSerializer.Serialize(validationResponse))
                };
                _logger.LogInformation($"perform handshake with eventGrid for function: {functionName}");
                return(returnMessage);
            }

            // Regular event processing
            if (string.Equals(eventTypeHeader, NotificationEvent, StringComparison.OrdinalIgnoreCase))
            {
                string requestContent = await req.Content.ReadAsStringAsync().ConfigureAwait(false);

                JToken token  = JToken.Parse(requestContent);
                JArray events = token.Type switch
                {
                    JTokenType.Array => (JArray)token,
                    JTokenType.Object => new JArray {
                        token
                    },
                    _ => throw new ArgumentOutOfRangeException(
                              $"The request content should be parseable into a JSON object or array, but was {token.Type}.")
                };

                return(await eventsFunc(events, functionName, cancellationToken).ConfigureAwait(false));
            }

            return(string.Equals(eventTypeHeader, UnsubscribeEvent, StringComparison.OrdinalIgnoreCase) ?
                   // TODO disable function?
                   new HttpResponseMessage(HttpStatusCode.Accepted) :
                   new HttpResponseMessage(HttpStatusCode.BadRequest));
        }
    }
        private async Task <HttpResponseMessage> ProcessAsync(HttpRequestMessage req)
        {
            // webjobs.script uses req.GetQueryNameValuePairs();
            // which requires webapi.core...but this does not work for .netframework2.0
            // TODO change this once webjobs.script is migrated
            var functionName = HttpUtility.ParseQueryString(req.RequestUri.Query)["functionName"];

            if (String.IsNullOrEmpty(functionName) || !_listeners.ContainsKey(functionName))
            {
                _tracer.Trace(new TraceEvent(System.Diagnostics.TraceLevel.Info,
                                             $"cannot find function: '{functionName}', available function names: [{string.Join(", ", _listeners.Keys.ToArray())}]"));
                return(new HttpResponseMessage(HttpStatusCode.NotFound)
                {
                    Content = new StringContent($"cannot find function: '{functionName}'")
                });
            }

            IEnumerable <string> eventTypeHeaders = null;
            string eventTypeHeader = null;

            if (req.Headers.TryGetValues("aeg-event-type", out eventTypeHeaders))
            {
                eventTypeHeader = eventTypeHeaders.First();
            }

            if (String.Equals(eventTypeHeader, "SubscriptionValidation", StringComparison.OrdinalIgnoreCase))
            {
                string jsonArray = await req.Content.ReadAsStringAsync();

                SubscriptionValidationEvent validationEvent = null;
                List <JObject> events = JsonConvert.DeserializeObject <List <JObject> >(jsonArray);
                // TODO remove unnecessary serialization
                validationEvent = ((JObject)events[0]["data"]).ToObject <SubscriptionValidationEvent>();
                SubscriptionValidationResponse validationResponse = new SubscriptionValidationResponse {
                    ValidationResponse = validationEvent.ValidationCode
                };
                var returnMessage = new HttpResponseMessage(HttpStatusCode.OK);
                returnMessage.Content = new StringContent(JsonConvert.SerializeObject(validationResponse));
                _tracer.Trace(new TraceEvent(System.Diagnostics.TraceLevel.Info,
                                             $"perform handshake with eventGrid for function: {functionName}"));
                return(returnMessage);
            }
            else if (String.Equals(eventTypeHeader, "Notification", StringComparison.OrdinalIgnoreCase))
            {
                JArray events         = null;
                string requestContent = await req.Content.ReadAsStringAsync();

                var token = JToken.Parse(requestContent);
                if (token.Type == JTokenType.Array)
                {
                    // eventgrid schema
                    events = (JArray)token;
                }
                else if (token.Type == JTokenType.Object)
                {
                    // cloudevent schema
                    events = new JArray
                    {
                        token
                    };
                }

                List <Task <FunctionResult> > executions = new List <Task <FunctionResult> >();
                foreach (var ev in events)
                {
                    // assume each event is a JObject
                    TriggeredFunctionData triggerData = new TriggeredFunctionData
                    {
                        TriggerValue = ev
                    };
                    executions.Add(_listeners[functionName].Executor.TryExecuteAsync(triggerData, CancellationToken.None));
                }
                await Task.WhenAll(executions);

                // FIXME without internal queuing, we are going to process all events in parallel
                // and return 500 if there's at least one failure...which will cause EventGrid to resend the entire payload
                foreach (var execution in executions)
                {
                    if (!execution.Result.Succeeded)
                    {
                        return(new HttpResponseMessage(HttpStatusCode.InternalServerError)
                        {
                            Content = new StringContent(execution.Result.Exception.Message)
                        });
                    }
                }

                return(new HttpResponseMessage(HttpStatusCode.Accepted));
            }
            else if (String.Equals(eventTypeHeader, "Unsubscribe", StringComparison.OrdinalIgnoreCase))
            {
                // TODO disable function?
                return(new HttpResponseMessage(HttpStatusCode.Accepted));
            }

            return(new HttpResponseMessage(HttpStatusCode.BadRequest));
        }