rosencrantz icon indicating copy to clipboard operation
rosencrantz copied to clipboard

Multi-valued header support

Open chrisheller opened this issue 4 years ago • 5 comments

The readHeaders procs in Rosencrantz are currently setup to work with single-valued headers, but don't handle multi-valued headers. This can be worked around by creating a copy of readHeaders that accepts HttpHeaderValues as the parameter type (instead of string).

proc readHeaders*(s1: string, p: proc(h1: HttpHeaderValues): Handler): Handler =
  proc h(req: ref Request, ctx: Context): Future[Context] {.async.} =
    if req.headers.hasKey(s1):
      let handler = p(req.headers[s1])
      let newCtx = await handler(req, ctx)
      return newCtx
    else:
      return ctx.reject()

  return h

In the readHeaders proc in Rosencrantz, the call works because req.headers[s1] (which is an HttpHeaderValues object) gets converted to a string by the toString() converter from the standard library's httpcore module. That converter just grabs the first value for the header though. That generally works since most headers are single-valued.

I'm not sure what would be the best way to add this now though. I tried changing the signature like this, but that wouldn't compile for some reason.

proc readHeaders*(s1: string, p: proc(h1: string|HttpHeaderValues): Handler): Handler =

Adding duplicate procs seems like the wrong way to deal with it though. Maybe a macro to handle duplication might work; I haven't tried that though.

chrisheller avatar Mar 01 '20 06:03 chrisheller

it could make sense to deprecate the string version and just use the HttpHeaderValues. When Rosencrantz was started, there was no such thing, and the stdlib httpserver only worked with non repeated headers. I will have a look in the next days

andreaferretti avatar Mar 01 '20 10:03 andreaferretti

While investigating this, I found a related issue in httpcore

andreaferretti avatar Mar 03 '20 10:03 andreaferretti

That said, I don't know what to do. The reason your approach does not work is that the or goes on the outside of the proc, not on the inside. That is, your version

proc readHeaders*(s1: string, p: proc(h1: string|HttpHeaderValues): Handler): Handler =

takes a p which accepts a string or an HttpHeaderValues, while the correct version is

proc readHeaders*(s1: string, p: (proc(h1: string): Handler) or (proc(h1: HttpHeaderValues): Handler)): Handler =

which takes either a p: string => Handler or a p: HttpHeaderValues => Handler.

Still, this does not work. I created a branch to track this - it compiles but the test fails. I am really not sure why - it may even be a bug in httpserver. I also tried the repeated version of the procs just to be sure, but it fails to read multiple headers anyway

andreaferretti avatar Mar 03 '20 10:03 andreaferretti

I submitted a PR to Nim to handle repeated values https://github.com/nim-lang/Nim/pull/13575

andreaferretti avatar Mar 03 '20 11:03 andreaferretti

Ok, it turns out it was a misunderstanding on my part. Now the above procs work with repeated headers in the repeated-headers branch.

@chrisheller If you want, please have a look if this works for you. If so, I will document and merge this

andreaferretti avatar Mar 06 '20 14:03 andreaferretti