NancyFx / Nancy

Lightweight, low-ceremony, framework for building HTTP based services on .Net and Mono
http://nancyfx.org
MIT License
7.15k stars 1.47k forks source link

Kestrel Exception on urlencoded paths when Content Negotiation adds links to responses #2695

Open csainty opened 7 years ago

csainty commented 7 years ago

Prerequisites

Description

When the path has urlencoded characters in it and content negotiation attempts to add link headers to the response. It does so in a manner that causes Kestrel to throw

System.InvalidOperationException: Invalid non-ASCII or control character in header: 0x00E5
   at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.FrameHeaders.ThrowInvalidHeaderCharacter(Char ch)
   at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.FrameHeaders.ValidateHeaderCharacters(String headerCharacters)
   at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.FrameHeaders.ValidateHeaderCharacters(StringValues headerValues)
   at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.FrameResponseHeaders.SetValueFast(String key, StringValues value)
   at Nancy.Owin.NancyMiddleware.RequestComplete(NancyContext context, IDictionary`2 environment, Func`2 performPassThrough, Func`2 next)

This happens because the characters that were encoded in the original url are passed unencoded to the response header, violating HTTP spec.

Steps to Reproduce

  1. Setup a plain Nancy project with Kestrel as the host
  2. Create a route Get("/{slug}", _ => new { Test = "Hello World"});
  3. curl -v http://localhost:5000/george-%C3%A5

System Configuration

As a workaround, you can return Response.AsJson() which bypasses the code to add links e.g. Get("/foo/{slug}", _ => Response.AsJson(new { Test = "Hello World"})); See https://github.com/NancyFx/Nancy/blob/master/src/Nancy/Responses/Negotiation/DefaultResponseNegotiator.cs#L42 for the bypass

By encoding a character that does not cause Kestrel to crash, you can see how it ends up unencoded in the response

$ curl -v http://localhost:5000/george-%3B
> GET /george-%3B HTTP/1.1
> Host: localhost:5000
> User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
> Accept: */*
> Referer:
>
< HTTP/1.1 200 OK
< Date: Mon, 30 Jan 2017 18:56:46 GMT
< Content-Type: application/json; charset=utf-8
< Server: Kestrel
< Transfer-Encoding: chunked
< Vary: Accept
< Link: </george-;.xml>; rel="alternate"; type="application/xml"
<
{"Test":"Hello World"}
khellang commented 6 years ago

Closed with #2903