/// <summary>
 /// Initializes a new <see cref="HttpExceptionHandler"/> for the specified HTTP
 /// <paramref name="request"/> and <paramref name="response"/>
 /// </summary>
 /// <param name="request">The HTTP request being processed</param>
 /// <param name="response">The HTTP response being constructed</param>
 /// <param name="source">(Optional) The object in which the exception occurred</param>
 /// <param name="diagnosticService">(Optional) The service through which diagnostic events
 /// are reported and processed</param>
 /// <exception cref="ArgumentNullException">Thrown if <paramref name="request"/> or
 /// <paramref name="response"/> are <c>null</c></exception>
 public HttpExceptionHandler(IHttpResourceRequest request, IHttpResourceResponse response, IDiagnosticService diagnosticService, object source = null)
 {
     _request           = request ?? throw new ArgumentNullException(nameof(request));
     _response          = response ?? throw new ArgumentNullException(nameof(response));
     _diagnosticService = diagnosticService ?? DiagnosticService.DefaultInstance;
     _source            = source ?? this;
 }
Beispiel #2
0
        /// <summary>
        /// Processes the specified <paramref name="request"/> and updates the supplied
        /// <paramref name="response"/>
        /// </summary>
        /// <param name="request">The HTTP resource request to process</param>
        /// <param name="response">The HTTP response to update</param>
        /// <param name="subPath">The portion of the request path that remains after the
        /// request was routed to this controller</param>
        /// <returns>Returns a task that completes when the request has been processed and the
        /// response has been updated</returns>
        public async Task Process(IHttpResourceRequest request, IHttpResourceResponse response,
                                  IEnumerable <string> subPath)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            var topicSegments = subPath.ToList();

            if (!topicSegments.Any() && request.IsGet())
            {
                await GetTopics(response);

                return;
            }

            if (topicSegments.Any())
            {
                var topic          = topicSegments.First();
                var nestedResource = topicSegments.Skip(1).FirstOrDefault();
                if ("subscriber".Equals(nestedResource, StringComparison.OrdinalIgnoreCase))
                {
                    await PostOrDeleteSubscriber(request, response, topic);

                    return;
                }
            }

            response.StatusCode = 400;
        }
Beispiel #3
0
        public async Task Process(IHttpResourceRequest request, IHttpResourceResponse response,
            IEnumerable<string> subPath)
        {
            if (request == null) throw new ArgumentNullException("request");
            if (response == null) throw new ArgumentNullException("response");

            var topicSegments = subPath.ToList();
            if (!topicSegments.Any() && "get".Equals(request.HttpMethod, StringComparison.OrdinalIgnoreCase))
            {
                await GetTopics(request, response);
                return;
            }
            if (topicSegments.Any())
            {
                var topic = topicSegments.First();
                var nestedResource = topicSegments.Skip(1).FirstOrDefault();
                if ("subscriber".Equals(nestedResource, StringComparison.OrdinalIgnoreCase))
                {
                    var subscriberSubPath = topicSegments.Skip(2);
                    await PostOrDeleteSubscriber(request, response, topic, subscriberSubPath);
                    return;
                }
            }

            response.StatusCode = 400;
        }
Beispiel #4
0
        /// <inheritdoc />
        public async Task Process(IHttpResourceRequest request, IHttpResourceResponse response,
                                  IEnumerable <string> subPath)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            if (!request.IsGet())
            {
                response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                response.AddHeader("Allow", "GET");
                return;
            }

            var metricsSegments = subPath.ToList();

            if (!metricsSegments.Any() && request.IsGet())
            {
                await GetMetrics(response);

                return;
            }

            response.StatusCode = 400;
        }
Beispiel #5
0
        private static MessageJournalFilter ConfigureFilter(IHttpResourceRequest request, ICollection <ErrorModel> errors)
        {
            var filter = new MessageJournalFilter();
            var topic  = request.QueryString["topic"];

            if (!string.IsNullOrWhiteSpace(topic))
            {
                filter.Topics = topic.Split(',')
                                .Select(t => (TopicName)t)
                                .ToList();
            }

            var category = request.QueryString["category"];

            if (!string.IsNullOrWhiteSpace(category))
            {
                filter.Categories = category.Split(',')
                                    .Select(t => (MessageJournalCategory)t.Trim())
                                    .ToList();
            }

            filter.From        = GetDateTime("from", request, errors);
            filter.To          = GetDateTime("to", request, errors);
            filter.Origination = GetUri("origination", request, errors);
            filter.Destination = GetUri("destination", request, errors);
            filter.MessageName = request.QueryString["messageName"];
            filter.RelatedTo   = GetMessageId("relatedTo", request, errors);

            return(filter);
        }
        /// <inheritdoc />
        /// <summary>
        /// Routes a <paramref name="request" /> and <paramref name="response" /> to
        /// the appropriate controller
        /// </summary>
        /// <param name="request">The request to route</param>
        /// <param name="response">The response to route</param>
        /// <returns>
        /// Returns a task that completes once the request has been routed and handled
        /// </returns>
        public async Task Route(IHttpResourceRequest request, IHttpResourceResponse response)
        {
            var requestPath = request.Url.AbsolutePath;

            if (string.IsNullOrWhiteSpace(requestPath))
            {
                return;
            }

            var pathSegments           = requestPath.Split('/');
            var supportedResourceTypes = ResourceTypes.ToList();
            var resourceSegments       = pathSegments
                                         .SkipWhile(segment => !supportedResourceTypes.Contains(segment))
                                         .ToList();

            var resourceType = resourceSegments.FirstOrDefault();

            if (string.IsNullOrWhiteSpace(resourceType))
            {
                response.StatusCode = 400;
                return;
            }

            var controller = GetController(resourceType);

            if (string.IsNullOrWhiteSpace(resourceType))
            {
                response.StatusCode = 400;
                return;
            }

            var subPath = resourceSegments.Skip(1); // Skip resource type
            await controller.Process(request, response, subPath);
        }
Beispiel #7
0
        private async Task PostOrDeleteSubscriber(IHttpResourceRequest request, IHttpResourceResponse response,
                                                  TopicName topic)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            var uri = request.QueryString["uri"];

            if (uri == null)
            {
                response.StatusCode        = 400;
                response.StatusDescription = "Subscriber URI is required";
                return;
            }

            var subscriber = new Uri(uri);

            if (!_topics.Contains(topic))
            {
                response.StatusCode        = 404;
                response.StatusDescription = "Topic not found";
                return;
            }

            var authorized = _authorizationService == null ||
                             await _authorizationService.IsAuthorizedToSubscribe(request.Principal, topic);

            if (!authorized)
            {
                response.StatusCode        = 401;
                response.StatusDescription = "Unauthorized";
                return;
            }

            if ("delete".Equals(request.HttpMethod, StringComparison.OrdinalIgnoreCase))
            {
                // TODO: Verify that the subscriber is the one deleting the subscription
                // Ideas: Issue another HTTP request back to the subscriber to verify?
                //        Require the subscriber to specify a subscription ID (GUID?)
                //        when subscribing and then supply the same ID when unsubscribing?
                await _subscriptionTrackingService.RemoveSubscription(topic, subscriber);
            }
            else
            {
                var ttlStr = request.QueryString["ttl"];
                var ttl    = string.IsNullOrWhiteSpace(ttlStr)
                    ? default(TimeSpan)
                    : TimeSpan.FromSeconds(int.Parse(ttlStr));

                await _subscriptionTrackingService.AddSubscription(topic, subscriber, ttl);
            }
            response.StatusCode = 200;
        }
Beispiel #8
0
        protected void GivenRequest(string method, NameValueCollection parameters = null)
        {
            var mockRequest = new Mock <IHttpResourceRequest>();

            mockRequest.Setup(x => x.HttpMethod).Returns(method);
            mockRequest.Setup(x => x.QueryString).Returns(parameters ?? new NameValueCollection());
            Request = mockRequest.Object;
        }
 public HttpExceptionHandler(IHttpResourceRequest request, IHttpResourceResponse response, ILog log = null)
 {
     if (request == null) throw new ArgumentNullException("request");
     if (response == null) throw new ArgumentNullException("response");
     _request = request;
     _response = response;
     _log = log ?? LogManager.GetLogger(LoggingCategories.Http);
 }
Beispiel #10
0
 public async Task GetTopics(IHttpResourceRequest request, IHttpResourceResponse response)
 {
     var topicList = _topics.Select(t => t.ToString()).ToArray();
     var responseContent = _serializer.Serialize(topicList);
     var encoding = response.ContentEncoding;
     var encodedContent = encoding.GetBytes(responseContent);
     await response.OutputStream.WriteAsync(encodedContent, 0, encodedContent.Length);
     response.StatusCode = 200;
 }
Beispiel #11
0
        public async Task Post(IHttpResourceRequest request, IHttpResourceResponse response)
        {
            if (request == null) throw new ArgumentNullException("request");
            if (response == null) throw new ArgumentNullException("response");

            var flattenedHeaders = request.Headers.AllKeys
                .ToDictionary(k => k, k => request.Headers[k]);

            var messageHeaders = new MessageHeaders(flattenedHeaders);
            var content = await request.ReadContentAsString();
            var message = new Message(messageHeaders, content);

            await _accept(message, request.Principal);
            response.StatusCode = 202;
        }
Beispiel #12
0
        public async Task Process(IHttpResourceRequest request, IHttpResourceResponse response,
            IEnumerable<string> subPath)
        {
            if (request == null) throw new ArgumentNullException("request");
            if (response == null) throw new ArgumentNullException("response");

            if (!request.IsPost())
            {
                response.StatusCode = 405;
                response.AddHeader("Allow", "POST");
                return;
            }

            await Post(request, response);
        }
        /// <summary>
        /// Reads the entire content of an HTTP resource request as a string
        /// </summary>
        /// <param name="request">The request</param>
        /// <returns>Returns a task whose result is the content of the <paramref name="request"/>
        /// as a string</returns>
        public static async Task <string> ReadContentAsString(this IHttpResourceRequest request)
        {
            if (request == null)
            {
                return(null);
            }

            var contentStream   = request.InputStream;
            var contentEncoding = request.ContentEncoding;

            using (var contentReader = new StreamReader(contentStream, contentEncoding))
            {
                return(await contentReader.ReadToEndAsync());
            }
        }
Beispiel #14
0
        private async Task <MessageJournalPosition> GetStartPosition(IHttpResourceRequest request, ICollection <ErrorModel> errors)
        {
            var startStr = request.QueryString["start"];

            try
            {
                return(string.IsNullOrWhiteSpace(startStr)
                    ? await _messageJournal.GetBeginningOfJournal()
                    : _messageJournal.ParsePosition(startStr));
            }
            catch (Exception)
            {
                errors.Add(new ErrorModel("Invalid start position", "start"));
                return(null);
            }
        }
        private async Task Post(IHttpResourceRequest request, IHttpResourceResponse response)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            var authorized = _authorizationService == null ||
                             await _authorizationService.IsAuthorizedToSendMessages(request.Principal);

            if (!authorized)
            {
                response.StatusCode        = 401;
                response.StatusDescription = "Unauthorized";
                return;
            }

            // The authorization header should have been processed by the HTTP host prior to this
            // point in order to initialize the principal on the request context.  This is
            // sensitive information and should not be copied into and thus exposed by the message
            // headers.
            var authorizationHeader = HttpRequestHeader.Authorization.ToString("G");
            var sensitiveHeaders    = new[] { authorizationHeader };

            var flattenedHeaders = request.Headers.AllKeys
                                   .Except(sensitiveHeaders, StringComparer.OrdinalIgnoreCase)
                                   .ToDictionary(k => k, k => request.Headers[k]);

            var messageHeaders = new MessageHeaders(flattenedHeaders)
            {
                Received = DateTime.UtcNow
            };

            var content = await request.ReadContentAsString();

            var message = new Message(messageHeaders, content);

            Thread.CurrentPrincipal = request.Principal;
            await _accept(message, request.Principal, default(CancellationToken));

            response.StatusCode = 202;
        }
Beispiel #16
0
        private static MessageId?GetMessageId(string parameter, IHttpResourceRequest request,
                                              ICollection <ErrorModel> errors)
        {
            var value = request.QueryString[parameter];

            if (!string.IsNullOrWhiteSpace(value))
            {
                if (!Guid.TryParse(value, out Guid guid))
                {
                    errors.Add(new ErrorModel("Invalid message ID: " + value, parameter));
                }
                else
                {
                    return(new MessageId(guid));
                }
            }
            return(null);
        }
Beispiel #17
0
        private static Uri GetUri(string parameter, IHttpResourceRequest request,
                                  ICollection <ErrorModel> errors)
        {
            var value = request.QueryString[parameter];

            if (!string.IsNullOrWhiteSpace(value))
            {
                if (!Uri.TryCreate(value, UriKind.Absolute, out Uri uri))
                {
                    errors.Add(new ErrorModel("Invalid URI: " + value, parameter));
                }
                else
                {
                    return(uri);
                }
            }
            return(null);
        }
Beispiel #18
0
        private static DateTime?GetDateTime(string parameter, IHttpResourceRequest request,
                                            ICollection <ErrorModel> errors)
        {
            var value = request.QueryString[parameter];

            if (!string.IsNullOrWhiteSpace(value))
            {
                if (!TryParseDate(value, out DateTime dateValue))
                {
                    errors.Add(new ErrorModel("Invalid date/time: " + value, parameter));
                }
                else
                {
                    return(dateValue);
                }
            }
            return(null);
        }
Beispiel #19
0
        private static int GetCount(IHttpResourceRequest request, ICollection <ErrorModel> errors)
        {
            var countStr = request.QueryString["count"];

            if (string.IsNullOrWhiteSpace(countStr))
            {
                errors.Add(new ErrorModel("Count is required", "count"));
                return(0);
            }

            if (!int.TryParse(countStr, out int count) || count <= 0)
            {
                errors.Add(new ErrorModel("Count must be a positive integer value", "count"));
                return(0);
            }

            return(count);
        }
        /// <inheritdoc />
        public async Task Process(IHttpResourceRequest request, IHttpResourceResponse response, IEnumerable <string> subPath)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            if (!request.IsPost())
            {
                response.StatusCode = 405;
                response.AddHeader("Allow", "POST");
                return;
            }

            await Post(request, response);
        }
Beispiel #21
0
        /// <inheritdoc />
        public async Task Process(IHttpResourceRequest request, IHttpResourceResponse response, IEnumerable <string> subPath)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            if (!request.IsGet())
            {
                response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                response.AddHeader("Allow", "GET");
                return;
            }



            await Get(request, response);
        }
Beispiel #22
0
        public async Task PostOrDeleteSubscriber(IHttpResourceRequest request, IHttpResourceResponse response,
            TopicName topic, IEnumerable<string> subPath)
        {
            if (request == null) throw new ArgumentNullException("request");
            if (response == null) throw new ArgumentNullException("response");

            var uri = request.QueryString["uri"];
            if (uri == null)
            {
                response.StatusCode = 400;
                response.StatusDescription = "Subscriber URI is required";
                return;
            }

            var subscriber = new Uri(uri);
            if (!_topics.Contains(topic))
            {
                response.StatusCode = 404;
                response.StatusDescription = "Topic not found";
                return;
            }

            if ("delete".Equals(request.HttpMethod, StringComparison.OrdinalIgnoreCase))
            {
                await _subscriptionTrackingService.RemoveSubscription(topic, subscriber);
            }
            else
            {
                var ttlStr = request.QueryString["ttl"];
                var ttl = string.IsNullOrWhiteSpace(ttlStr)
                    ? default(TimeSpan)
                    : TimeSpan.FromSeconds(int.Parse(ttlStr));

                await _subscriptionTrackingService.AddSubscription(topic, subscriber, ttl);
            }
            response.StatusCode = 202;
        }
 /// <summary>
 /// Indicates whether a <paramref name="request"/> is a GET method request
 /// </summary>
 /// <param name="request">The request</param>
 /// <returns>Returns <c>true</c> if the request is a GET method request</returns>
 public static bool IsGet(this IHttpResourceRequest request)
 {
     return(request != null && "GET".Equals(request.HttpMethod, StringComparison.OrdinalIgnoreCase));
 }
Beispiel #24
0
        private async Task Get(IHttpResourceRequest request, IHttpResourceResponse response)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            if (_messageJournal == null)
            {
                // Message journaling is not enabled
                response.StatusCode = (int)HttpStatusCode.NotImplemented;
                return;
            }

            var authorized = _authorizationService == null ||
                             await _authorizationService.IsAuthorizedToQueryJournal(request.Principal);

            if (!authorized)
            {
                response.StatusCode        = (int)HttpStatusCode.Unauthorized;
                response.StatusDescription = "Unauthorized";
                return;
            }

            var responseModel = new JournalGetResponseModel();
            var start         = await GetStartPosition(request, responseModel.Errors);

            var count  = GetCount(request, responseModel.Errors);
            var filter = ConfigureFilter(request, responseModel.Errors);

            if (responseModel.Errors.Any())
            {
                response.StatusCode = (int)HttpStatusCode.BadRequest;
            }
            else
            {
                var result = await _messageJournal.Read(start, count, filter);

                responseModel.Start        = start.ToString();
                responseModel.Next         = result.Next.ToString();
                responseModel.EndOfJournal = result.EndOfJournal;
                responseModel.Entries      = result.Entries.Select(entry => new MessageJournalEntryModel
                {
                    Position  = entry.Position.ToString(),
                    Category  = entry.Category,
                    Timestamp = entry.Timestamp,
                    Data      = new MessageJournalEntryDataModel
                    {
                        Headers = entry.Data.Headers.ToDictionary(h => (string)h.Key, h => h.Value),
                        Content = entry.Data.Content
                    }
                }).ToList();
                response.StatusCode = (int)HttpStatusCode.OK;
            }

            response.ContentType = "application/json";
            var encoding = response.ContentEncoding;

            if (encoding == null)
            {
                encoding = Encoding.UTF8;
                response.ContentEncoding = encoding;
            }

            var serializedContent = _serializer.Serialize(responseModel);
            var encodedContent    = encoding.GetBytes(serializedContent);

            response.StatusCode = 200;
            await response.OutputStream.WriteAsync(encodedContent, 0, encodedContent.Length);
        }