/// <summary>
        /// Negotiates the response based on the given result and context.
        /// </summary>
        /// <param name="routeResult">The route result.</param>
        /// <param name="context">The context.</param>
        /// <returns>A <see cref="Response" />.</returns>
        public Response NegotiateResponse(dynamic routeResult, NancyContext context)
        {
            Response response;

            if (TryCastResultToResponse(routeResult, out response))
            {
                context.WriteTraceLog(sb =>
                                      sb.AppendLine("[DefaultResponseNegotiator] Processing as real response"));

                return(response);
            }

            context.WriteTraceLog(sb =>
                                  sb.AppendLine("[DefaultResponseNegotiator] Processing as negotiation"));

            NegotiationContext negotiationContext = GetNegotiationContext(routeResult, context);

            var coercedAcceptHeaders = this.GetCoercedAcceptHeaders(context).ToArray();

            context.WriteTraceLog(sb => GetAccepHeaderTraceLog(context, negotiationContext, coercedAcceptHeaders, sb));

            var compatibleHeaders = this.GetCompatibleHeaders(coercedAcceptHeaders, negotiationContext, context).ToArray();

            if (!compatibleHeaders.Any())
            {
                context.WriteTraceLog(sb =>
                                      sb.AppendLine("[DefaultResponseNegotiator] Unable to negotiate response - no headers compatible"));

                return(new NotAcceptableResponse());
            }

            return(CreateResponse(compatibleHeaders, negotiationContext, context));
        }
        /// <summary>
        /// Creates a response from the compatible headers.
        /// </summary>
        /// <param name="compatibleHeaders">The compatible headers.</param>
        /// <param name="negotiationContext">The negotiation context.</param>
        /// <param name="context">The context.</param>
        /// <returns>A <see cref="Response"/>.</returns>
        private Response CreateResponse(IList <CompatibleHeader> compatibleHeaders,
                                        NegotiationContext negotiationContext,
                                        NancyContext context)
        {
            var response = NegotiateResponse(compatibleHeaders, negotiationContext, context);

            if (response == null)
            {
                context.WriteTraceLog(sb =>
                                      sb.AppendLine("[DefaultResponseNegotiator] Unable to negotiate response - no processors returned valid response"));

                response = new NotAcceptableResponse();
            }

            response.WithHeader("Vary", "Accept");

            this.AddLinkHeader(compatibleHeaders, response, context.Request.Url);
            SetStatusCode(negotiationContext, response);
            SetReasonPhrase(negotiationContext, response);
            AddCookies(negotiationContext, response);

            if (response is NotAcceptableResponse)
            {
                return(response);
            }

            AddContentTypeHeader(negotiationContext, response);
            AddNegotiatedHeaders(negotiationContext, response);

            return(response);
        }
        /// <summary>
        /// Prioritizes the response processors and tries to negotiate a response.
        /// </summary>
        /// <param name="compatibleHeaders">The compatible headers.</param>
        /// <param name="negotiationContext">The negotiation context.</param>
        /// <param name="context">The context.</param>
        /// <returns>Response.</returns>
        private static Response NegotiateResponse(
            IEnumerable <CompatibleHeader> compatibleHeaders,
            NegotiationContext negotiationContext,
            NancyContext context)
        {
            foreach (var compatibleHeader in compatibleHeaders)
            {
                var prioritizedProcessors = compatibleHeader.Processors
                                            .OrderByDescending(x => x.Item2.ModelResult)
                                            .ThenByDescending(x => x.Item2.RequestedContentTypeResult);

                foreach (var prioritizedProcessor in prioritizedProcessors)
                {
                    var processorType = prioritizedProcessor.Item1.GetType();

                    context.WriteTraceLog(sb =>
                                          sb.AppendFormat("[DefaultResponseNegotiator] Invoking processor: {0}\n", processorType));

                    var mediaRangeModel = negotiationContext.GetModelForMediaRange(compatibleHeader.MediaRange);

                    var response = prioritizedProcessor.Item1.Process(compatibleHeader.MediaRange, mediaRangeModel, context);
                    if (response != null)
                    {
                        return(response);
                    }
                }
            }

            return(null);
        }
 /// <summary>
 /// Adds the negotiated headers from the <see cref="NegotiationContext"/> to the <see cref="Response"/>.
 /// </summary>
 /// <param name="negotiationContext">The negotiation context.</param>
 /// <param name="response">The response.</param>
 private static void AddNegotiatedHeaders(NegotiationContext negotiationContext, Response response)
 {
     foreach (var header in negotiationContext.Headers)
     {
         response.Headers[header.Key] = header.Value;
     }
 }
 /// <summary>
 /// Sets the status code from the <see cref="NegotiationContext"/> on the <see cref="Response"/>.
 /// </summary>
 /// <param name="negotiationContext">The negotiation context.</param>
 /// <param name="response">The response.</param>
 private static void SetStatusCode(NegotiationContext negotiationContext, Response response)
 {
     if (negotiationContext.StatusCode.HasValue)
     {
         response.StatusCode = negotiationContext.StatusCode.Value;
     }
 }
 /// <summary>
 /// Sets the reason phrase from the <see cref="NegotiationContext"/> on the <see cref="Response"/>.
 /// </summary>
 /// <param name="negotiationContext">The negotiation context.</param>
 /// <param name="response">The response.</param>
 private static void SetReasonPhrase(NegotiationContext negotiationContext, Response response)
 {
     if (negotiationContext.ReasonPhrase != null)
     {
         response.ReasonPhrase = negotiationContext.ReasonPhrase;
     }
 }
 /// <summary>
 /// Adds the cookies from the <see cref="NegotiationContext"/> to the <see cref="Response"/>.
 /// </summary>
 /// <param name="negotiationContext">The negotiation context.</param>
 /// <param name="response">The response.</param>
 private static void AddCookies(NegotiationContext negotiationContext, Response response)
 {
     foreach (var cookie in negotiationContext.Cookies)
     {
         response.Cookies.Add(cookie);
     }
 }
 /// <summary>
 /// Adds the content type header from the <see cref="NegotiationContext"/> to the <see cref="Response"/>.
 /// </summary>
 /// <param name="negotiationContext">The negotiation context.</param>
 /// <param name="response">The response.</param>
 private static void AddContentTypeHeader(NegotiationContext negotiationContext, Response response)
 {
     if (negotiationContext.Headers.ContainsKey("Content-Type"))
     {
         response.ContentType = negotiationContext.Headers["Content-Type"];
         negotiationContext.Headers.Remove("Content-Type");
     }
 }
 /// <summary>
 /// Adds the content type header from the <see cref="NegotiationContext"/> to the <see cref="Response"/>.
 /// </summary>
 /// <param name="negotiationContext">The negotiation context.</param>
 /// <param name="response">The response.</param>
 private static void AddContentTypeHeader(NegotiationContext negotiationContext, Response response)
 {
     if (negotiationContext.Headers.ContainsKey("Content-Type"))
     {
         response.ContentType = negotiationContext.Headers["Content-Type"];
         negotiationContext.Headers.Remove("Content-Type");
     }
 }
        /// <summary>
        /// Adds the content type header from the <see cref="NegotiationContext"/> to the <see cref="Response"/>.
        /// </summary>
        /// <param name="negotiationContext">The negotiation context.</param>
        /// <param name="response">The response.</param>
        private static void AddContentTypeHeader(NegotiationContext negotiationContext, Response response)
        {
            string contentType;

            if (negotiationContext.Headers.TryGetValue("Content-Type", out contentType))
            {
                response.ContentType = contentType;
                negotiationContext.Headers.Remove("Content-Type");
            }
        }
        private static IEnumerable <Tuple <string, decimal> > GetCompatibleHeaders(
            IEnumerable <Tuple <string, decimal> > coercedAcceptHeaders,
            NegotiationContext negotiationContext)
        {
            var permissableMediaRanges = negotiationContext.PermissableMediaRanges;

            if (permissableMediaRanges.Any(mr => mr.IsWildcard))
            {
                return(coercedAcceptHeaders.Where(header => header.Item2 > 0m));
            }

            return(coercedAcceptHeaders
                   .Where(header => header.Item2 > 0m)
                   .SelectMany(header => permissableMediaRanges
                               .Where(mr => mr.Matches(header.Item1))
                               .Select(mr => Tuple.Create(mr.ToString(), header.Item2))));
        }
        private IEnumerable <CompatibleHeader> GetCompatibleHeaders(
            IEnumerable <Tuple <string, decimal> > coercedAcceptHeaders,
            NegotiationContext negotiationContext,
            NancyContext context)
        {
            var acceptHeaders = GetCompatibleHeaders(coercedAcceptHeaders, negotiationContext);

            foreach (var header in acceptHeaders)
            {
                var mediaRangeModel = negotiationContext.GetModelForMediaRange(header.Item1);

                IEnumerable <Tuple <IResponseProcessor, ProcessorMatch> > compatibleProcessors =
                    this.GetCompatibleProcessorsByHeader(header.Item1, mediaRangeModel, context);

                if (compatibleProcessors.Any())
                {
                    yield return(new CompatibleHeader(header.Item1, compatibleProcessors));
                }
            }
        }
        private static void GetAccepHeaderTraceLog(
            NancyContext context,
            NegotiationContext negotiationContext,
            Tuple <string, decimal>[] coercedAcceptHeaders,
            StringBuilder sb)
        {
            var allowableFormats = negotiationContext.PermissableMediaRanges
                                   .Select(mr => mr.ToString())
                                   .Aggregate((t1, t2) => t1 + ", " + t2);

            var originalAccept = context.Request.Headers["accept"].Any()
                ? string.Join(", ", context.Request.Headers["accept"])
                : "None";

            var coercedAccept = coercedAcceptHeaders.Any()
                ? coercedAcceptHeaders.Select(h => h.Item1).Aggregate((t1, t2) => t1 + ", " + t2)
                : "None";

            sb.AppendFormat("[DefaultResponseNegotiator] Original accept header: {0}\n", originalAccept);
            sb.AppendFormat("[DefaultResponseNegotiator] Coerced accept header: {0}\n", coercedAccept);
            sb.AppendFormat("[DefaultResponseNegotiator] Acceptable media ranges: {0}\n", allowableFormats);
        }
        public void Should_reset_negotiation_context()
        {
            // Given
            var context = new NancyContext();
            var negotiationContext = new NegotiationContext { ViewName = "Index" };
            context.NegotiationContext = negotiationContext;

            // When
            this.statusCodeHandler.Handle(HttpStatusCode.InternalServerError, context);

            // Then
            Assert.Equal(context.NegotiationContext.ViewName, null);
        }
        private static void GetAccepHeaderTraceLog(
            NancyContext context,
            NegotiationContext negotiationContext,
            Tuple<string, decimal>[] coercedAcceptHeaders,
            StringBuilder sb)
        {
            var allowableFormats = negotiationContext.PermissableMediaRanges
                .Select(mr => mr.ToString())
                .Aggregate((t1, t2) => t1 + ", " + t2);

            var originalAccept = context.Request.Headers["accept"].Any()
                ? string.Join(", ", context.Request.Headers["accept"])
                : "None";

            var coercedAccept = coercedAcceptHeaders.Any()
                ? coercedAcceptHeaders.Select(h => h.Item1).Aggregate((t1, t2) => t1 + ", " + t2)
                : "None";

            sb.AppendFormat("[DefaultResponseNegotiator] Original accept header: {0}\n", originalAccept);
            sb.AppendFormat("[DefaultResponseNegotiator] Coerced accept header: {0}\n", coercedAccept);
            sb.AppendFormat("[DefaultResponseNegotiator] Acceptable media ranges: {0}\n", allowableFormats);
        }
        /// <summary>
        /// Creates a response from the compatible headers.
        /// </summary>
        /// <param name="compatibleHeaders">The compatible headers.</param>
        /// <param name="negotiationContext">The negotiation context.</param>
        /// <param name="context">The context.</param>
        /// <returns>A <see cref="Response"/>.</returns>
        private static Response CreateResponse(
            IList<CompatibleHeader> compatibleHeaders,
            NegotiationContext negotiationContext,
            NancyContext context)
        {
            var response = NegotiateResponse(compatibleHeaders, negotiationContext, context);

            if (response == null)
            {
                context.WriteTraceLog(sb =>
                    sb.AppendLine("[DefaultResponseNegotiator] Unable to negotiate response - no processors returned valid response"));

                response = new NotAcceptableResponse();
            }

            response.WithHeader("Vary", "Accept");

            AddLinkHeader(compatibleHeaders, response, context.Request.Url);
            SetStatusCode(negotiationContext, response);
            SetReasonPhrase(negotiationContext, response);
            AddCookies(negotiationContext, response);

            if (response is NotAcceptableResponse)
            {
                return response;
            }

            AddContentTypeHeader(negotiationContext, response);
            AddNegotiatedHeaders(negotiationContext, response);

            return response;
        }
        /// <summary>
        /// Prioritizes the response processors and tries to negotiate a response.
        /// </summary>
        /// <param name="compatibleHeaders">The compatible headers.</param>
        /// <param name="negotiationContext">The negotiation context.</param>
        /// <param name="context">The context.</param>
        /// <returns>Response.</returns>
        private static Response NegotiateResponse(
            IEnumerable<CompatibleHeader> compatibleHeaders,
            NegotiationContext negotiationContext,
            NancyContext context)
        {
            foreach (var compatibleHeader in compatibleHeaders)
            {
                var prioritizedProcessors = compatibleHeader.Processors
                    .OrderByDescending(x => x.Item2.ModelResult)
                    .ThenByDescending(x => x.Item2.RequestedContentTypeResult);

                foreach (var prioritizedProcessor in prioritizedProcessors)
                {
                    var processorType = prioritizedProcessor.Item1.GetType();

                    context.WriteTraceLog(sb =>
                        sb.AppendFormat("[DefaultResponseNegotiator] Invoking processor: {0}\n", processorType));

                    var mediaRangeModel = negotiationContext.GetModelForMediaRange(compatibleHeader.MediaRange);

                    var response = prioritizedProcessor.Item1.Process(compatibleHeader.MediaRange, mediaRangeModel, context);
                    if (response != null)
                    {
                        return response;
                    }
                }
            }

            return null;
        }
        private static IEnumerable<Tuple<string, decimal>> GetCompatibleHeaders(
            IEnumerable<Tuple<string, decimal>> coercedAcceptHeaders,
            NegotiationContext negotiationContext)
        {
            var permissableMediaRanges = negotiationContext.PermissableMediaRanges;
            if (permissableMediaRanges.Any(mr => mr.IsWildcard))
            {
                return coercedAcceptHeaders.Where(header => header.Item2 > 0m);
            }

            return coercedAcceptHeaders
                .Where(header => header.Item2 > 0m)
                .SelectMany(header => permissableMediaRanges
                    .Where(mr => mr.Matches(header.Item1))
                    .Select(mr => Tuple.Create(mr.ToString(), header.Item2)));
        }
 /// <summary>
 /// Sets the reason phrase from the <see cref="NegotiationContext"/> on the <see cref="Response"/>.
 /// </summary>
 /// <param name="negotiationContext">The negotiation context.</param>
 /// <param name="response">The response.</param>
 private static void SetReasonPhrase(NegotiationContext negotiationContext, Response response)
 {
     if (negotiationContext.ReasonPhrase != null)
     {
         response.ReasonPhrase = negotiationContext.ReasonPhrase;
     }
 }
 /// <summary>
 /// Sets the status code from the <see cref="NegotiationContext"/> on the <see cref="Response"/>.
 /// </summary>
 /// <param name="negotiationContext">The negotiation context.</param>
 /// <param name="response">The response.</param>
 private static void SetStatusCode(NegotiationContext negotiationContext, Response response)
 {
     if (negotiationContext.StatusCode.HasValue)
     {
         response.StatusCode = negotiationContext.StatusCode.Value;
     }
 }
        private IEnumerable<CompatibleHeader> GetCompatibleHeaders(
            IEnumerable<Tuple<string, decimal>> coercedAcceptHeaders,
            NegotiationContext negotiationContext,
            NancyContext context)
        {
            var acceptHeaders = GetCompatibleHeaders(coercedAcceptHeaders, negotiationContext);

            foreach (var header in acceptHeaders)
            {
                var mediaRangeModel = negotiationContext.GetModelForMediaRange(header.Item1);

                IEnumerable<Tuple<IResponseProcessor, ProcessorMatch>> compatibleProcessors =
                    this.GetCompatibleProcessorsByHeader(header.Item1, mediaRangeModel, context);

                if (compatibleProcessors.Any())
                {
                    yield return new CompatibleHeader(header.Item1, compatibleProcessors);
                }
            }
        }
 /// <summary>
 /// Adds the cookies from the <see cref="NegotiationContext"/> to the <see cref="Response"/>.
 /// </summary>
 /// <param name="negotiationContext">The negotiation context.</param>
 /// <param name="response">The response.</param>
 private static void AddCookies(NegotiationContext negotiationContext, Response response)
 {
     foreach (var cookie in negotiationContext.Cookies)
     {
         response.Cookies.Add(cookie);
     }
 }
 /// <summary>
 /// Adds the negotiated headers from the <see cref="NegotiationContext"/> to the <see cref="Response"/>.
 /// </summary>
 /// <param name="negotiationContext">The negotiation context.</param>
 /// <param name="response">The response.</param>
 private static void AddNegotiatedHeaders(NegotiationContext negotiationContext, Response response)
 {
     foreach (var header in negotiationContext.Headers)
     {
         response.Headers[header.Key] = header.Value;
     }
 }