Skip to content

Commit

Permalink
Merge pull request #516 from Banashek/485-accept-all-header-fix
Browse files Browse the repository at this point in the history
Updating mimetype accept header parsing to use builtin aspnet parse/methods
  • Loading branch information
dustinmoris authored Jul 7, 2022
2 parents 29d18c8 + 6039a6a commit afe3cf9
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 2 deletions.
7 changes: 5 additions & 2 deletions src/Giraffe/Core.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Core =
open System.Globalization
open Microsoft.AspNetCore.Http
open Microsoft.Extensions.Logging
open Microsoft.Net.Http.Headers
open Giraffe.ViewEngine

/// <summary>
Expand Down Expand Up @@ -227,11 +228,13 @@ module Core =
/// <param name="ctx"></param>
/// <returns>A Giraffe <see cref="HttpHandler"/> function which can be composed into a bigger web application.</returns>
let mustAccept (mimeTypes : string list) : HttpHandler =
let acceptedMimeTypes : MediaTypeHeaderValue list = mimeTypes |> List.map (MediaTypeHeaderValue.Parse)
fun (next : HttpFunc) (ctx : HttpContext) ->
let headers = ctx.Request.GetTypedHeaders()
headers.Accept
|> Seq.map (fun h -> h.ToString())
|> Seq.exists (fun h -> mimeTypes |> Seq.contains h)
|> Seq.exists (fun h ->
acceptedMimeTypes
|> List.exists (fun amt -> amt.IsSubsetOf(h)))
|> function
| true -> next ctx
| false -> skipPipeline
Expand Down
62 changes: 62 additions & 0 deletions tests/Giraffe.Tests/HttpHandlerTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,68 @@ let ``POST "/either" with unsupported Accept header returns 404 "Not found"`` ()
Assert.Equal(404, ctx.Response.StatusCode)
}

[<Fact>]
let ``POST with "all-medias" header type returns the first available route`` () =
/// Reference: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2
let ctx = Substitute.For<HttpContext>()
let app =
choose [
POST >=> choose [
route "/any" >=> mustAccept [ "text/plain" ] >=> text "first route"
route "/any" >=> mustAccept [ "application/json" ] >=> json "second route"
route "/any" >=> mustAccept [ "text/plain"; "application/json" ] >=> text "third route" ]
setStatusCode 404 >=> text "Not found" ]

let headers = HeaderDictionary()
headers.Add("Accept", StringValues("*/*"))
ctx.Request.Method.ReturnsForAnyArgs "POST" |> ignore
ctx.Request.Path.ReturnsForAnyArgs (PathString("/any")) |> ignore
ctx.Request.Headers.ReturnsForAnyArgs(headers) |> ignore
ctx.Response.Body <- new MemoryStream()
let expected = "first route"

task {
let! result = app next ctx

match result with
| None -> assertFail $"Result was expected to be %s{expected}"
| Some ctx ->
let body = getBody ctx
Assert.Equal(expected, body)
Assert.Equal("text/plain; charset=utf-8", ctx.Response |> getContentType)
}

[<Fact>]
let ``POST with an accept header type containing a fuzzy type and concrete subtype returns the first matching route`` () =
/// Reference: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2
let ctx = Substitute.For<HttpContext>()
let app =
choose [
POST >=> choose [
route "/any" >=> mustAccept [ "text/plain" ] >=> text "first route"
route "/any" >=> mustAccept [ "application/xml" ] >=> text "<test>second route</test>"
route "/any" >=> mustAccept [ "text/plain"; "application/json" ] >=> text "third route" ]
setStatusCode 404 >=> text "Not found" ]

let headers = HeaderDictionary()
headers.Add("Accept", StringValues("application/*"))
ctx.Request.Method.ReturnsForAnyArgs "POST" |> ignore
ctx.Request.Path.ReturnsForAnyArgs (PathString("/any")) |> ignore
ctx.Request.Headers.ReturnsForAnyArgs(headers) |> ignore
ctx.Response.Body <- new MemoryStream()
let expected = "<test>second route</test>"

task {
let! result = app next ctx

match result with
| None -> assertFail $"Result was expected to be %s{expected}"
| Some ctx ->
let body = getBody ctx
Assert.Equal(expected, body)
Assert.Equal("text/plain; charset=utf-8", ctx.Response |> getContentType)
}

[<Fact>]
let ``GET "/person" returns rendered HTML view`` () =
let ctx = Substitute.For<HttpContext>()
Expand Down

0 comments on commit afe3cf9

Please sign in to comment.