Fix HttpHandler, HttpHandlerFactory and HttpServerChannel to work properly with a...
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Http / HttpClientChannel.cs
index 165216b2ec8e1aaf4d162b0d8612a65bc1fb6739..e92b059b435b8b9422ea9ea54fae9aa88efa52dc 100644 (file)
 // (C) 2003 Martin Willemoes Hansen
 //
 
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using System.Collections;
 using System.IO;
@@ -28,22 +49,12 @@ using System.Text;
 
 namespace System.Runtime.Remoting.Channels.Http
 {
-
-
-
-
-
-       public class HttpClientChannel : IChannelSender,IChannel
+       public class HttpClientChannel : BaseChannelWithProperties, IChannelSender,IChannel
        {
                // Property Keys (purposely all lower-case)
                private const String ProxyNameKey = "proxyname";
                private const String ProxyPortKey = "proxyport";
 
-               // If above keys get modified be sure to modify, the KeySet property on this
-               // class.
-               private static ICollection s_keySet = null;
-        
-    
                // Settings
                private int    _channelPriority = 1;  // channel priority
                private String _channelName = "http"; // channel name
@@ -57,68 +68,55 @@ namespace System.Runtime.Remoting.Channels.Http
                private int _clientConnectionLimit = 0; // bump connection limit to at least this number (only meaningful if > 0)
                private bool _bUseDefaultCredentials = false; // should default credentials be used?
         
-               private IClientChannelSinkProvider _sinkProvider = null; // sink chain provider         
-        
-               private IMessageSink _msgSink;
-
-
+               private IClientChannelSinkProvider _sinkProvider = null; // sink chain provider
 
                public HttpClientChannel()
                {
-                       
-                       SetupProvider(_sinkProvider);
+                       SetupProvider (null,null);
                } 
-
-
                
                public HttpClientChannel(String name, IClientChannelSinkProvider sinkProvider)
                {
                        if(name != null)
                                _channelName = name;
                        
-                       SetupProvider(sinkProvider);
+                       SetupProvider (sinkProvider, null);
                }
-       
 
                // constructor used by config file
                public HttpClientChannel(IDictionary properties, IClientChannelSinkProvider sinkProvider)
                {       
-                       if(properties!=null)
-                       foreach(DictionaryEntry Dict in properties)
+                       if (properties != null) 
                        {
-                               switch(Dict.Key.ToString())
+                               foreach(DictionaryEntry Dict in properties)
                                {
-                                       case "name":
-                                               _channelName = Dict.Value.ToString();
-                                               break;
-                                       case "priority":
-                                               _channelPriority = Convert.ToInt32(Dict.Value);
-                                               break;
-                                       case "clientConnectionLimit":
-                                               _clientConnectionLimit = Convert.ToInt32(Dict.Value);
-                                               break;
-                                       case "proxyName":
-                                               _proxyName = Dict.Value.ToString();
-                                               break;
-                                       case "proxyPort":
-                                               _proxyPort = Convert.ToInt32(Dict.Value);
-                                               break;
-                                       case "useDefaultCredentials":
-                                               _bUseDefaultCredentials = Convert.ToBoolean(Dict.Value);
-                                               break;
+                                       switch(Dict.Key.ToString())
+                                       {
+                                               case "name":
+                                                       _channelName = Dict.Value.ToString();
+                                                       break;
+                                               case "priority":
+                                                       _channelPriority = Convert.ToInt32(Dict.Value);
+                                                       break;
+                                               case "clientConnectionLimit":
+                                                       _clientConnectionLimit = Convert.ToInt32(Dict.Value);
+                                                       break;
+                                               case "proxyName":
+                                                       _proxyName = Dict.Value.ToString();
+                                                       break;
+                                               case "proxyPort":
+                                                       _proxyPort = Convert.ToInt32(Dict.Value);
+                                                       break;
+                                               case "useDefaultCredentials":
+                                                       _bUseDefaultCredentials = Convert.ToBoolean(Dict.Value);
+                                                       break;
+                                       }
                                }
                        }
-                       //UpdateProxy();
-                       SetupProvider(sinkProvider);
-
+                       
+                       SetupProvider (sinkProvider, properties);
                } 
         
-               
-               /*<note><private>
-               private void SetupChannel()
-               {
-               } */
-
                public int ChannelPriority
                {
                        get { return _channelPriority; }    
@@ -147,7 +145,7 @@ namespace System.Runtime.Remoting.Channels.Http
 
                public virtual IMessageSink CreateMessageSink(String url, Object remoteChannelData, out String objectURI)
                {
-                       if (url == null && remoteChannelData != null && remoteChannelData as IChannelDataStore != null )
+                       if ((url == null || !HttpHelper.StartsWithHttp (url)) && remoteChannelData != null && remoteChannelData as IChannelDataStore != null )
                        {
                                IChannelDataStore ds = (IChannelDataStore) remoteChannelData;
                                url = ds.ChannelUris[0];
@@ -156,19 +154,18 @@ namespace System.Runtime.Remoting.Channels.Http
                        if(url != null && HttpHelper.StartsWithHttp(url))
                        {
                                HttpHelper.Parse(url, out objectURI);
-                               _msgSink = (IMessageSink) _sinkProvider.CreateSink(this,url,remoteChannelData); 
+                               IMessageSink msgSink = (IMessageSink) _sinkProvider.CreateSink(this,url,remoteChannelData); 
                                
-                               if(_msgSink !=null )
+                               if(msgSink !=null )
                                        SetServicePoint(url);
 
-                               return _msgSink;
+                               return msgSink;
                        }
                        else
                        {
                                objectURI = null;
                                return null;
                        }
-
                }
 
                private void UpdateProxy()
@@ -190,22 +187,21 @@ namespace System.Runtime.Remoting.Channels.Http
                        ServicePoint sp = ServicePointManager.FindServicePoint(channelURI,ProxyObject);
                        if(_clientConnectionLimit> 0)
                                sp.ConnectionLimit = _clientConnectionLimit;
-                               
-                       
-               }
-
-               
+               }               
 
                internal IWebProxy ProxyObject { get { return _proxyObject; } }
                internal bool UseDefaultCredentials { get { return _bUseDefaultCredentials; } }
 
-               private void SetupProvider(IClientChannelSinkProvider sinkProvider)
+               private void SetupProvider (IClientChannelSinkProvider sinkProvider, IDictionary properties)
                {
+                       if (properties == null) properties = new Hashtable ();
+                       HttpClientTransportSinkProvider httpSink = new HttpClientTransportSinkProvider (properties);
+                       SinksWithProperties = httpSink;
+                       
                        if(sinkProvider == null)
                        {
-                               //_sinkProvider = new SoapClientFormatterSinkProvider();
-                               _sinkProvider  = new BinaryClientFormatterSinkProvider();
-                               _sinkProvider.Next = new HttpClientTransportSinkProvider();
+                               _sinkProvider = new SoapClientFormatterSinkProvider();
+                               _sinkProvider.Next = httpSink;
                        }
                        else
                        {
@@ -217,17 +213,30 @@ namespace System.Runtime.Remoting.Channels.Http
                                        dummySinkProvider = dummySinkProvider.Next;
                                }
 
-                               dummySinkProvider.Next = new HttpClientTransportSinkProvider();
+                               dummySinkProvider.Next = httpSink;
                        } 
-                       
+               }
+               
+               public override object this [object key]
+               {
+                       get { return Properties[key]; }
+                       set { Properties[key] = value; }
+               }
+               
+               public override ICollection Keys 
+               {
+                       get { return Properties.Keys; }
                }
        } 
 
 
-       internal class HttpClientTransportSinkProvider : IClientChannelSinkProvider
+       internal class HttpClientTransportSinkProvider : IClientChannelSinkProvider, IChannelSinkBase
        {
-               internal HttpClientTransportSinkProvider()
+               IDictionary _properties;
+               
+               internal HttpClientTransportSinkProvider (IDictionary properties)
                {
+                       _properties = properties;
                }    
    
                public IClientChannelSink CreateSink(IChannelSender channel, String url, 
@@ -242,6 +251,12 @@ namespace System.Runtime.Remoting.Channels.Http
                        get { return null; }
                        set { throw new NotSupportedException(); }
                }
+               
+               public IDictionary Properties
+               {
+                       get { return _properties; }
+               }
+               
        } // class HttpClientTransportSinkProvider
 
 
@@ -253,8 +268,7 @@ namespace System.Runtime.Remoting.Channels.Http
                private const String s_defaultVerb = "POST";
 
                private static String s_userAgent =
-                       "Mozilla/4.0+(compatible; MSIE 6.0; Windows " + 
-                       "; MS .NET Remoting; MS .NET CLR " + System.Environment.Version.ToString() + " )";
+                       "Mono Remoting Client (Mono CLR " + System.Environment.Version.ToString() + ")";
         
                // Property keys (purposely all lower-case)
                private const String UserNameKey = "username";
@@ -293,7 +307,7 @@ namespace System.Runtime.Remoting.Channels.Http
 
                // settings
                private bool _useChunked = false; 
-               private bool _useKeepAlive = true;
+//             private bool _useKeepAlive = true;
 
                internal HttpClientTransportSink(HttpClientChannel channel, String channelURI) : base()
                {
@@ -307,37 +321,29 @@ namespace System.Runtime.Remoting.Channels.Http
                        ITransportHeaders requestHeaders, Stream requestStream,
                        out ITransportHeaders responseHeaders, out Stream responseStream)
                {
-                       
-
                        string url = null;
-                       string uri = (string)requestHeaders[CommonTransportKeys.RequestUri];
+                       string uri = ((IMethodCallMessage)msg).Uri;
+                       requestHeaders [CommonTransportKeys.RequestUri] = uri;
                        CreateUrl(uri,out url);
 
-                       HttpWebRequest httpWebRequest = CreateWebRequest(url,requestHeaders,requestStream);
+                       HttpWebRequest httpWebRequest = CreateWebRequest (url,requestHeaders,requestStream);
 
-                       SendAndRecieve(httpWebRequest,out responseHeaders,out responseStream);
+                       SendAndRecieve (httpWebRequest,out responseHeaders,out responseStream);
                }
 
 
                public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg,
                        ITransportHeaders headers, Stream stream)
                {
-                       try
-                       {
-                               string url = null;
-                               string uri = (string)headers[CommonTransportKeys.RequestUri];
-                               CreateUrl(uri,out url);
-
-                               HttpWebRequest httpWebRequest = CreateWebRequest(url,headers,stream);
-                               RequestState reqState = new RequestState(httpWebRequest,sinkStack);
+                       string url = null;
+                       string uri = ((IMethodCallMessage)msg).Uri;
+                       headers [CommonTransportKeys.RequestUri] = uri;
+                       CreateUrl(uri,out url);
 
-                               httpWebRequest.BeginGetResponse(new AsyncCallback(AsyncRequestHandler),reqState);
+                       HttpWebRequest httpWebRequest = CreateWebRequest(url,headers,stream);
+                       RequestState reqState = new RequestState(httpWebRequest,sinkStack);
 
-                       }
-                       catch
-                       {
-                               Console.WriteLine("Error Sending Async Request");
-                       }
+                       httpWebRequest.BeginGetResponse(new AsyncCallback(AsyncRequestHandler),reqState);
                }
 
                private void AsyncRequestHandler(IAsyncResult ar)
@@ -346,36 +352,29 @@ namespace System.Runtime.Remoting.Channels.Http
 
                        RequestState reqState = (RequestState) ar.AsyncState;
                        HttpWebRequest httpWebRequest = reqState.webRquest;
+                       IClientChannelSinkStack sinkStack = reqState.sinkStack;
 
                        try
                        {
                                httpWebResponse = (HttpWebResponse) httpWebRequest.EndGetResponse(ar);
-                               
-                               Stream responseStream ;
-                               ITransportHeaders responseHeaders;
-
-                               IClientChannelSinkStack sinkStack = reqState.sinkStack;
+                       }
+                       catch (WebException ex)
+                       {
+                               httpWebResponse = ex.Response as HttpWebResponse;
+                               if (httpWebResponse == null) sinkStack.DispatchException (ex);
+                       }
 
-                               responseStream = new MemoryStream();
-                               HttpHelper.CopyStream(httpWebResponse.GetResponseStream(),ref responseStream);
-                       
-                       
-                               responseHeaders = new TransportHeaders();
-                       
-                               for(int i=0; i < httpWebResponse.Headers.Count; ++i)
-                                       responseHeaders[httpWebResponse.Headers.Keys[i].ToString()] = httpWebResponse.Headers[i].ToString();
+                       Stream responseStream;
+                       ITransportHeaders responseHeaders;
 
-                               sinkStack.AsyncProcessResponse(responseHeaders,responseStream);
-                                       
-                       }
-                       catch
+                       try
                        {
-                               Console.WriteLine("Error Recieving Async Response");
+                               ReceiveResponse (httpWebResponse, out responseHeaders, out responseStream);
+                               sinkStack.AsyncProcessResponse(responseHeaders,responseStream);
                        }
-                       finally
+                       catch (Exception ex)
                        {
-                               if(httpWebResponse!=null)
-                                       httpWebResponse.Close();        
+                               sinkStack.DispatchException (ex);
                        }
                }
 
@@ -456,7 +455,6 @@ namespace System.Runtime.Remoting.Channels.Http
                        }
                } // this[]   
         
-
                public override ICollection Keys
                {
                        get
@@ -483,9 +481,6 @@ namespace System.Runtime.Remoting.Channels.Http
                        }
                } 
 
-
-               
-               
                private void UpdateProxy()
                        {
                                // If the user values for the proxy object are valid , then the proxy
@@ -534,10 +529,6 @@ namespace System.Runtime.Remoting.Channels.Http
 
                private HttpWebRequest CreateWebRequest(string url, ITransportHeaders requestHeaders, Stream requestStream)
                {
-
-                       
-
-                       
                        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);;
                        request.AllowAutoRedirect = _bAllowAutoRedirect;
                        request.ContentLength = requestStream.Length;
@@ -545,7 +536,7 @@ namespace System.Runtime.Remoting.Channels.Http
                        //request.Expect = "100-Continue";
                        
                        //This caused us some troubles with the HttpWebResponse class
-                       //maybe its fixed now.
+                       //maybe its fixed now. TODO
                        //request.KeepAlive = _useKeepAlive;
                        request.KeepAlive = false;;
                        
@@ -576,57 +567,72 @@ namespace System.Runtime.Remoting.Channels.Http
                        }
 
                        Stream reqStream = request.GetRequestStream();
-                       HttpHelper.CopyStream(requestStream,ref reqStream);
-                       
-                       reqStream.Close();
+                       try {
+                               HttpHelper.CopyStream(requestStream, reqStream);
+                       } finally {
+                               reqStream.Close();
+                       }
                        
                        return request;
                }       
         
-               private bool SendAndRecieve(HttpWebRequest httpRequest,out ITransportHeaders responseHeaders,out Stream responseStream)
+               private void SendAndRecieve(HttpWebRequest httpRequest,out ITransportHeaders responseHeaders,out Stream responseStream)
                {
                        responseStream = null;
                        responseHeaders = null;
                        HttpWebResponse httpWebResponse = null;
 
-                       bool returnValue = false;
-
-
                        try
                        {
                                httpWebResponse = (HttpWebResponse)httpRequest.GetResponse();
+                       }
+                       catch (WebException ex)
+                       {
+                               httpWebResponse = ex.Response as HttpWebResponse;
+                               if (httpWebResponse == null || httpWebResponse.StatusCode == HttpStatusCode.InternalServerError) throw ex;
+                       }
 
-                               responseStream = new MemoryStream();
-                               HttpHelper.CopyStream(httpWebResponse.GetResponseStream(),ref responseStream);
+                       ReceiveResponse (httpWebResponse, out responseHeaders, out responseStream);
+               }
 
+               private void ReceiveResponse (HttpWebResponse httpWebResponse, out ITransportHeaders responseHeaders, out Stream responseStream)
+               {
+                       responseHeaders = new TransportHeaders();
+                       Stream webStream = httpWebResponse.GetResponseStream();
+
+                       try
+                       {
+                               if (httpWebResponse.ContentLength != -1)
+                               {
+                                       byte[] buffer = new byte [httpWebResponse.ContentLength];
+                                       int nr = 0;
+                                       while (nr < buffer.Length) {
+                                               int pr = webStream.Read (buffer, nr, buffer.Length - nr);
+                                               if (pr == 0) throw new RemotingException ("Connection closed");
+                                               nr += pr;
+                                       }
+                                       responseStream = new MemoryStream (buffer);
+                               }
+                               else
+                               {
+                                       responseStream = new MemoryStream();
+                                       HttpHelper.CopyStream(webStream, responseStream);
+                               }
 
-                               responseHeaders = new TransportHeaders();
-                               
-                               
                                //Use the two commented lines below instead of the 3 below lines when HttpWebResponse
                                //class is fully implemented in order to support custom headers
                                //for(int i=0; i < httpWebResponse.Headers.Count; ++i)
                                //      responseHeaders[httpWebResponse.Headers.Keys[i].ToString()] = httpWebResponse.Headers[i].ToString();
-                               
+
                                responseHeaders["Content-Type"] = httpWebResponse.ContentType;
                                responseHeaders["Server"] = httpWebResponse.Server;
                                responseHeaders["Content-Length"] = httpWebResponse.ContentLength;
-                       
-                               returnValue =true;
-                       }
-                       catch(Exception e)
-                       {
-                               
-                               Console.WriteLine(e);
-                               returnValue = false;
                        }
-
                        finally
                        {
-                               if(httpWebResponse!=null)
-                                       httpWebResponse.Close();                        
+                               webStream.Close ();
+                               httpWebResponse.Close();
                        }
-                       return returnValue;
                }
 
                private void ProcessErrorCode()