Multiple Headers with the same name

Oct 1, 2008 at 9:44 PM
Hello again,

As part of the response, multiple headers with the same name can be sent, especially Set-Cookie where one Set-Cookie header is required for every cookie. However, as you are working with a NameValueCollection, there is no possibility for that? Do you have any idea on how to fix that?

X-Powered-By: PHP/5.2.3
Set-Cookie: PHPSESSID=tqaj82eqeci5obv5gsj7ar1mi5; expires=Thu, 02-Oct-2008 20:27:03 GMT; path=/
Set-Cookie: lngltw=fr_CA; expires=Mon, 30-Mar-2009 20:27:03 GMT; path=/
X-Powered-By: ASP.NET

----

        public void AddHeader(string name, string value)
        {
            if (HeadersSent)
                throw new InvalidOperationException("Headers have already been sent.");

            for (int i = 1; i < value.Length; ++i )
            {
                if (value[i] == '\r' && !char.IsWhiteSpace(value[i-1]))
                    throw new ArgumentException("New line in value do not start with a white space.");
                if (value[i] == '\n' && value[i-1] != '\r')
                    throw new ArgumentException("Invalid new line sequence, should be \\r\\n (crlf).");
            }
                _headers[name.ToLower()] = value;
        }
Coordinator
Oct 2, 2008 at 7:40 AM
The RFC says:

Multiple message-header fields with the same field-name MAY be present in a message if and only if the entire field-value for that header field is defined as a comma-separated list [i.e., #(values)]. It MUST be possible to combine the multiple header fields into one "field-name: field-value" pair, without changing the semantics of the message, by appending each subsequent field-value to the first, each separated by a comma. The order in which header fields with the same field-name are received is therefore significant to the interpretation of the combined field value, and thus a proxy MUST NOT change the order of these field values when a message is forwarded.

That works fine with Powered by:
X-Powered-By: PHP/5.2.3, ASP.NET

Cookies however is a problem, since they can contain comma. And that's why the Response.Cookies collection exist. All cookies will be sent on different lines.

Although I've now modified the header sending so all headers are sent on different lines (no API change), else I have to check if a comma exists in the header values.
Download the latest code revision.
Oct 2, 2008 at 9:19 PM
I've downloaded the latest code revision. There is a bug. Instead of having key: value, I get value: value.

I think I've found the problem:

            for (int i = 0; i < _headers.Count; ++i)
            {
                string headerName = _headers[i];
                string[] values =_headers.GetValues(i);
                if (values == null) continue;
                foreach (string value in values)
                    sb.AppendFormat("{0}: {1}\r\n", headerName, value);
            }

Should be:

            for (int i = 0; i < _headers.Count; ++i)
            {
                string headerName = _headers.AllKeys[i];
                string[] values =_headers.GetValues(i);
                if (values == null) continue;
                foreach (string value in values)
                    sb.AppendFormat("{0}: {1}\r\n", headerName, value);
            }

Thanks for your help!

Vincent
Oct 2, 2008 at 9:34 PM
I have one problem with the Response.Cookies collection. There is no constructor that just accepts raw cookie text and that spits out the same raw text in ToString.

The problem is that PHP CGI returns many Set-Cookie: val headers which are best sent out as-is instead of parsing them and then recreating them using ResponseCookie.ToString().

What do you think about that? Do you want me to implement it?

Vincent


Oct 3, 2008 at 9:06 AM
(int i = 0; i < _headers.Count; ++i)
            {
                string headerName = _headers.AllKeys[i];
                string[] values =_headers.GetValues(i);
                if (values == null) continue;
                foreach (string value in values)
                    sb.AppendFormat("{0}: {1}\r\n", headerName, value);
            }


Has now been fixed, thank you for supplying a solution.

Regarding the constructor in ResponseCookies it would still require the ResponseCookies to parse the string since it saves ResponseCookie instances internally.
Instead of using the ResponseCookies.Add you could just add the cookie to the headers in the response and it will be regarded the same.

So try:
phpCookies = phpCookies.SubString(0, phpCookies.FirstIndexOf(' ')); // remove the Set-Cookie:  from the phpcookie string
Response.Headers.Add("Set-Cookie", phpCookies);


Almund