yaws icon indicating copy to clipboard operation
yaws copied to clipboard

Make yaws:accumulate_header/1 conforming to RFC

Open leoliu opened this issue 3 years ago • 4 comments

Many headers allow you to sent as multiple headers with the same name for example, Vary, Cache-Control, Accept-Encoding, Set-Cookie etc etc. Not all of them are handled correctly.

I discovered this discrepancy while experimenting with the Vary header. In my case a request is passed through multiple functions. One function adds a Vary header for security reasons but the header is gone because a later function accidentally overrides it owing to yaws:accumulate_header/1's last-write-win semantics.

There is also the issue of not normalising the header names. For example, "Vary" and "vary" are different headers per Yaws.

leoliu avatar Mar 11 '21 01:03 leoliu

If you have multiple functions trying to contribute headers, you might consider using yaws_api:merge_header/2,3 instead:

    merge_header(Headers, {Header, Value})
          Merges value Value for header Header with any existing value for
          that  header in the #headers{} record Headers, and returns a new
          #headers{} record. Using the atom  undefined  for  Value  simply
          returns  Headers.  Otherwise,  Value is merged with any existing
          value already present in the Headers record for  header  Header,
          comma-separated  from  that  existing  value.  If  no such value
          exists in  the  Headers  record,  the  effect  is  the  same  as
          set_header/2.  Note  that  for the Set-Cookie header, values are
          not comma-separated but  are  instead  collected  into  a  tuple
          {multi,  ValueList}  where  ValueList  is the collection of Set-
          Cookie values. This implies that any formatting  fun  passed  to
          reformat_header/2 must be prepared to handle such tuples.

   merge_header(Headers, Header, Value)
          Same  as  merge_header/2  above, except Header and Value are not
          passed in a tuple.

Regarding "Vary" vs "vary", Yaws performs normalization of header names. Additionally, for some common headers like "Vary", you can use the atom vary so that normalization isn't an issue. If you can provide example code that produces "Vary" and "vary" together, or at least describe how to reproduce it, I'll take a look.

vinoski avatar Mar 11 '21 02:03 vinoski

Sorry my original report may not be clear but I am referring to response headers.

leoliu avatar Mar 11 '21 03:03 leoliu

Sure, but as you probably know Yaws supports multiple approaches for server applications functionality, including .yaws pages, appmods, ehtml data, and more. It would be helpful if you could explain more about what your code is doing.

vinoski avatar Mar 11 '21 11:03 vinoski

To simplify imagine an out/1 function that goes through a chain of many functions but eventually returns:

[...
 {header, {vary, "text1"}},
 ...
    ...
       {header, {vary, "text2"}},
 ...
]

Only the last vary header will be sent back because of the faulty last-write-win by yaws:accumulate_header/1.

leoliu avatar Mar 11 '21 22:03 leoliu