Merge pull request #2305 from ludovic-henry/fix-threadpool-36414
[mono.git] / mcs / class / System / System.Net / HttpListenerResponse.cs
index e7716fa397162f21ebb9f0c51c3cdfce19f04093..dcc553036e1aea112b7e5d6f864e92e58e84e119 100644 (file)
@@ -26,7 +26,7 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-#if NET_2_0 && SECURITY_DEP
+#if SECURITY_DEP
 
 using System.Globalization;
 using System.IO;
@@ -49,7 +49,10 @@ namespace System.Net {
                string status_description = "OK";
                bool chunked;
                HttpListenerContext context;
+               
                internal bool HeadersSent;
+               internal object headers_lock = new object ();
+               
                bool force_close_chunked;
 
                internal HttpListenerResponse (HttpListenerContext context)
@@ -331,9 +334,8 @@ namespace System.Net {
 
                void Close (bool force)
                {
-                       // TODO: use the 'force' argument
-                       context.Connection.Close ();
                        disposed = true;
+                       context.Connection.Close (force);
                }
 
                public void Close ()
@@ -392,16 +394,14 @@ namespace System.Net {
                        return false;
                }
 
-               internal void SendHeaders (bool closing)
+               internal void SendHeaders (bool closing, MemoryStream ms)
                {
-                       //TODO: When do we send KeepAlive?
-                       MemoryStream ms = new MemoryStream ();
                        Encoding encoding = content_encoding;
                        if (encoding == null)
                                encoding = Encoding.Default;
 
                        if (content_type != null) {
-                               if (content_encoding != null && content_type.IndexOf ("charset=") == -1) {
+                               if (content_encoding != null && content_type.IndexOf ("charset=", StringComparison.Ordinal) == -1) {
                                        string enc_name = content_encoding.WebName;
                                        headers.SetInternal ("Content-Type", content_type + "; charset=" + enc_name);
                                } else {
@@ -444,51 +444,99 @@ namespace System.Net {
                                        status_code == 503);
 
                        if (conn_close == false)
-                               conn_close = (context.Request.Headers ["connection"] == "close");
+                               conn_close = !context.Request.KeepAlive;
 
                        // They sent both KeepAlive: true and Connection: close!?
-                       if (!keep_alive || conn_close)
+                       if (!keep_alive || conn_close) {
                                headers.SetInternal ("Connection", "close");
+                               conn_close = true;
+                       }
 
                        if (chunked)
                                headers.SetInternal ("Transfer-Encoding", "chunked");
 
-                       int chunked_uses = context.Connection.ChunkedUses;
-                       if (chunked_uses >= 100) {
+                       int reuses = context.Connection.Reuses;
+                       if (reuses >= 100) {
                                force_close_chunked = true;
-                               if (!conn_close)
+                               if (!conn_close) {
                                        headers.SetInternal ("Connection", "close");
+                                       conn_close = true;
+                               }
+                       }
+
+                       if (!conn_close) {
+                               headers.SetInternal ("Keep-Alive", String.Format ("timeout=15,max={0}", 100 - reuses));
+                               if (context.Request.ProtocolVersion <= HttpVersion.Version10)
+                                       headers.SetInternal ("Connection", "keep-alive");
                        }
 
                        if (location != null)
                                headers.SetInternal ("Location", location);
 
                        if (cookies != null) {
-                               bool firstDone = false;
-                               StringBuilder cookieSB = new StringBuilder ();
-                               foreach (Cookie cookie in cookies) {
-                                       if (firstDone)
-                                               cookieSB.Append (",");
-                                       firstDone = true;
-                                       cookieSB.Append (cookie.ToClientString ());
-                               }
-                               headers.SetInternal("Set-Cookie2", cookieSB.ToString ());
+                               foreach (Cookie cookie in cookies)
+                                       headers.SetInternal ("Set-Cookie", CookieToClientString (cookie));
                        }
 
-                       StreamWriter writer = new StreamWriter (ms, encoding);
+                       StreamWriter writer = new StreamWriter (ms, encoding, 256);
                        writer.Write ("HTTP/{0} {1} {2}\r\n", version, status_code, status_description);
-                       string headers_str = headers.ToString ();
+                       string headers_str = headers.ToStringMultiValue ();
                        writer.Write (headers_str);
                        writer.Flush ();
-                       // Perf.: use TCP_CORK if we're writing more?
-                       int preamble = encoding.GetPreamble ().Length;
+                       int preamble = (encoding.CodePage == 65001) ? 3 : encoding.GetPreamble ().Length;
                        if (output_stream == null)
                                output_stream = context.Connection.GetResponseStream ();
 
-                       output_stream.InternalWrite (ms.GetBuffer (), 0 + preamble, (int) ms.Length - preamble);
+                       /* Assumes that the ms was at position 0 */
+                       ms.Position = preamble;
                        HeadersSent = true;
                }
 
+               static string CookieToClientString (Cookie cookie)
+               {
+                       if (cookie.Name.Length == 0)
+                               return String.Empty;
+
+                       StringBuilder result = new StringBuilder (64);
+
+                       if (cookie.Version > 0)
+                               result.Append ("Version=").Append (cookie.Version).Append (";");
+
+                       result.Append (cookie.Name).Append ("=").Append (cookie.Value);
+
+                       if (cookie.Path != null && cookie.Path.Length != 0)
+                               result.Append (";Path=").Append (QuotedString (cookie, cookie.Path));
+
+                       if (cookie.Domain != null && cookie.Domain.Length != 0)
+                               result.Append (";Domain=").Append (QuotedString (cookie, cookie.Domain));                       
+
+                       if (cookie.Port != null && cookie.Port.Length != 0)
+                               result.Append (";Port=").Append (cookie.Port);  
+
+                       return result.ToString ();
+               }
+
+               static string QuotedString (Cookie cookie, string value)
+               {
+                       if (cookie.Version == 0 || IsToken (value))
+                               return value;
+                       else 
+                               return "\"" + value.Replace("\"", "\\\"") + "\"";
+               }       
+
+               static string tspecials = "()<>@,;:\\\"/[]?={} \t";   // from RFC 2965, 2068
+
+           static bool IsToken (string value) 
+               {
+                       int len = value.Length;
+                       for (int i = 0; i < len; i++) {
+                           char c = value [i];
+                               if (c < 0x20 || c >= 0x7f || tspecials.IndexOf (c) != -1)
+                                       return false;
+                       }
+                       return true;
+               }
+
                public void SetCookie (Cookie cookie)
                {
                        if (cookie == null)