public WebPubSubListener(ITriggeredFunctionExecutor executor, string listenerKey, IWebPubSubTriggerDispatcher dispatcher, WebPubSubValidationOptions validationOptions)
 {
     _dispatcher       = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
     _listenerKey      = listenerKey ?? throw new ArgumentNullException(nameof(listenerKey));
     Executor          = executor ?? throw new ArgumentNullException(nameof(executor));
     ValidationOptions = validationOptions;
 }
        public static async Task <WebPubSubEventRequest> ReadWebPubSubRequestAsync(this HttpRequest request, WebPubSubValidationOptions options)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            // validation request.
            if (request.IsValidationRequest(out var requestHosts))
            {
                if (options == null || !options.ContainsHost())
                {
                    return(new PreflightRequest(true));
                }
                else
                {
                    foreach (var item in requestHosts)
                    {
                        if (options.ContainsHost(item))
                        {
                            return(new PreflightRequest(true));
                        }
                    }
                }
                return(new PreflightRequest(false));
            }

            if (!request.TryParseCloudEvents(out var context))
            {
                throw new ArgumentException("Invalid Web PubSub upstream request missing required fields in header.");
            }

            if (!context.IsValidSignature(options))
            {
                throw new UnauthorizedAccessException("Signature validation failed.");
            }

            var requestType = context.GetRequestType();

            switch (requestType)
            {
            case RequestType.Connect:
            {
                var content      = await new StreamReader(request.Body).ReadToEndAsync().ConfigureAwait(false);
                var eventRequest = JsonSerializer.Deserialize <ConnectEventRequest>(content);
                return(new ConnectEventRequest(context, eventRequest.Claims, eventRequest.Query, eventRequest.Subprotocols, eventRequest.ClientCertificates));
            }

            case RequestType.User:
            {
                using var ms = new MemoryStream();
                await request.Body.CopyToAsync(ms).ConfigureAwait(false);

                var data = BinaryData.FromBytes(ms.ToArray());
                if (!MediaTypeHeaderValue.Parse(request.ContentType).MediaType.IsValidMediaType(out var dataType))
                {
                    throw new ArgumentException($"ContentType is not supported: {request.ContentType}");
                }
                return(new UserEventRequest(context, data, dataType));
            }

            case RequestType.Connected:
            {
                return(new ConnectedEventRequest(context));
            }

            case RequestType.Disconnected:
            {
                var content      = await new StreamReader(request.Body).ReadToEndAsync().ConfigureAwait(false);
                var eventRequest = JsonSerializer.Deserialize <DisconnectedEventRequest>(content);
                return(new DisconnectedEventRequest(context, eventRequest.Reason));
            }

            default:
                return(null);
            }
        }
        internal static bool IsValidSignature(this WebPubSubConnectionContext connectionContext, WebPubSubValidationOptions options)
        {
            // no options skip validation.
            if (options == null || !options.ContainsHost())
            {
                return(true);
            }

            if (options.TryGetKey(connectionContext.Origin, out var accessKey))
            {
                // server side disable signature checks.
                if (string.IsNullOrEmpty(accessKey))
                {
                    return(true);
                }

                var signatures = connectionContext.Signature.ToHeaderList();
                if (signatures == null)
                {
                    return(false);
                }
                using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(accessKey));
                var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(connectionContext.ConnectionId));
                var hash      = "sha256=" + BitConverter.ToString(hashBytes).Replace("-", "");
                if (signatures.Contains(hash, StringComparer.OrdinalIgnoreCase))
                {
                    return(true);
                }
            }
            return(false);
        }