Merge pull request #2624 from ludovic-henry/monoerror-appdomain
[mono.git] / mcs / class / System / System.Net / FileWebRequest.cs
index 99d9f24e3293503443384b1d39110aa38781e574..618c09ee145cb5ed92053efb2ab6dcc07b15a423 100644 (file)
@@ -5,10 +5,33 @@
 //   Lawrence Pit (loz@cable.a2000.nl)\r
 //\r
 \r
+//\r
+// Permission is hereby granted, free of charge, to any person obtaining\r
+// a copy of this software and associated documentation files (the\r
+// "Software"), to deal in the Software without restriction, including\r
+// without limitation the rights to use, copy, modify, merge, publish,\r
+// distribute, sublicense, and/or sell copies of the Software, and to\r
+// permit persons to whom the Software is furnished to do so, subject to\r
+// the following conditions:\r
+// \r
+// The above copyright notice and this permission notice shall be\r
+// included in all copies or substantial portions of the Software.\r
+// \r
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+//\r
+\r
 using System;\r
 using System.Collections;\r
 using System.IO;\r
 using System.Runtime.Serialization;\r
+using System.Runtime.Remoting.Messaging;\r
+using System.Threading;\r
 \r
 namespace System.Net \r
 {\r
@@ -20,46 +43,56 @@ namespace System.Net
                \r
                private ICredentials credentials;\r
                private string connectionGroup;\r
-               private string method;\r
-               private int timeout;\r
-               private bool open = false;\r
+               private long contentLength;\r
+               private FileAccess fileAccess = FileAccess.Read;\r
+               private string method = "GET";\r
+               private IWebProxy proxy;\r
+               private bool preAuthenticate;\r
+               private int timeout = 100000;\r
+               \r
+               private Stream requestStream;\r
+               private FileWebResponse webResponse;\r
+               private AutoResetEvent requestEndEvent;\r
+               private bool requesting;\r
+               private bool asyncResponding;\r
                \r
                // Constructors\r
                \r
                internal FileWebRequest (Uri uri) \r
                { \r
                        this.uri = uri;\r
-                       this.webHeaders = new WebHeaderCollection ();\r
-                       this.method = "GET";\r
-                       this.timeout = System.Threading.Timeout.Infinite; \r
-               }               \r
+                       this.webHeaders = new WebHeaderCollection ();\r
+               }\r
                \r
-               [MonoTODO]\r
+               [Obsolete ("Serialization is obsoleted for this type", false)]\r
                protected FileWebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext) \r
                {\r
-                       throw new NotImplementedException ();\r
+                       SerializationInfo info = serializationInfo;\r
+                       webHeaders = (WebHeaderCollection) info.GetValue ("headers", typeof (WebHeaderCollection));\r
+                       proxy = (IWebProxy) info.GetValue ("proxy", typeof (IWebProxy));\r
+                       uri = (Uri) info.GetValue ("uri", typeof (Uri));\r
+                       connectionGroup = info.GetString ("connectionGroupName");\r
+                       method = info.GetString ("method");\r
+                       contentLength = info.GetInt64 ("contentLength");\r
+                       timeout = info.GetInt32 ("timeout");\r
+                       fileAccess = (FileAccess) info.GetValue ("fileAccess", typeof (FileAccess));\r
+                       preAuthenticate = info.GetBoolean ("preauthenticate");\r
                }\r
                \r
                // Properties\r
                \r
                // currently not used according to spec\r
-               public override string ConnectionGroupName { \r
+               public override string ConnectionGroupName {\r
                        get { return connectionGroup; }\r
                        set { connectionGroup = value; }\r
                }\r
                \r
-               public override long ContentLength { \r
-                       get { \r
-                               try {\r
-                                       return Int64.Parse (webHeaders ["Content-Length"]); \r
-                               } catch (Exception) {\r
-                                       return 0;\r
-                               }\r
-                       }\r
-                       set { \r
+               public override long ContentLength {\r
+                       get { return contentLength; }\r
+                       set {\r
                                if (value < 0)\r
-                                       throw new ArgumentException ("value");\r
-                               webHeaders ["Content-Length"] = Convert.ToString (value);\r
+                                       throw new ArgumentException ("The Content-Length value must be greater than or equal to zero.", "value");\r
+                               contentLength =  value;\r
                        }\r
                }\r
                \r
@@ -80,19 +113,24 @@ namespace System.Net
                // currently not used according to spec\r
                public override string Method { \r
                        get { return this.method; }\r
-                       set { this.method = value; }\r
+                       set {\r
+                               if (value == null || value.Length == 0)\r
+                                       throw new ArgumentException ("Cannot set null or blank "\r
+                                               + "methods on request.", "value");\r
+                               this.method = value;\r
+                       }\r
                }\r
                \r
                // currently not used according to spec\r
                public override bool PreAuthenticate { \r
-                       get { throw new NotSupportedException (); }\r
-                       set { throw new NotSupportedException (); }\r
+                       get { return preAuthenticate; }\r
+                       set { preAuthenticate = value; }\r
                }\r
                \r
                // currently not used according to spec\r
-               public override IWebProxy Proxy { \r
-                       get { throw new NotSupportedException (); }\r
-                       set { throw new NotSupportedException (); }\r
+               public override IWebProxy Proxy {\r
+                       get { return proxy; }\r
+                       set { proxy = value; }\r
                }\r
                \r
                public override Uri RequestUri { \r
@@ -102,86 +140,178 @@ namespace System.Net
                public override int Timeout { \r
                        get { return timeout; }\r
                        set { \r
-                               if (value < 0)\r
-                                       throw new ArgumentException ("value");\r
+                               if (value < -1)\r
+                                       throw new ArgumentOutOfRangeException ("Timeout can be "\r
+                                               + "only set to 'System.Threading.Timeout.Infinite' "\r
+                                               + "or a value >= 0.");\r
                                timeout = value;\r
                        }\r
                }\r
-               \r
+\r
+               public override bool UseDefaultCredentials\r
+               {\r
+                       get {\r
+                               throw new NotSupportedException ();\r
+                       }\r
+                       set {\r
+                               throw new NotSupportedException ();\r
+                       }\r
+               }\r
                // Methods\r
                \r
-               [MonoTODO]\r
-               public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state) \r
+               private delegate Stream GetRequestStreamCallback ();\r
+               private delegate WebResponse GetResponseCallback ();\r
+\r
+               static Exception GetMustImplement ()\r
                {\r
-                       throw new NotImplementedException ();\r
+                       return new NotImplementedException ();\r
                }\r
                \r
+               /* LAMESPEC: Docs suggest this was present in 1.1 and\r
+                * 1.0 profiles, but the masterinfos say otherwise\r
+                */\r
                [MonoTODO]\r
-               public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)\r
+               public override void Abort ()\r
                {\r
-                       throw new NotImplementedException ();\r
+                       throw GetMustImplement ();\r
                }\r
 \r
-               [MonoTODO]\r
+               public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state) \r
+               {\r
+                       if (string.Compare ("GET", method, true) == 0 ||\r
+                               string.Compare ("HEAD", method, true) == 0 ||\r
+                               string.Compare ("CONNECT", method, true) == 0)\r
+                               throw new ProtocolViolationException ("Cannot send a content-body with this verb-type.");\r
+                       lock (this) {\r
+                               if (asyncResponding || webResponse != null)\r
+                                       throw new InvalidOperationException ("This operation cannot be performed after the request has been submitted.");\r
+                               if (requesting)\r
+                                       throw new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");\r
+                               requesting = true;\r
+                       }\r
+                       GetRequestStreamCallback c = new GetRequestStreamCallback (this.GetRequestStreamInternal);\r
+                       return c.BeginInvoke (callback, state);\r
+               }\r
+               \r
                public override Stream EndGetRequestStream (IAsyncResult asyncResult)\r
                {\r
-                       throw new NotImplementedException ();\r
+                       if (asyncResult == null)\r
+                               throw new ArgumentNullException ("asyncResult");\r
+                       if (!asyncResult.IsCompleted)\r
+                               asyncResult.AsyncWaitHandle.WaitOne ();\r
+                       AsyncResult async = (AsyncResult) asyncResult;\r
+                       GetRequestStreamCallback cb = (GetRequestStreamCallback) async.AsyncDelegate;\r
+                       return cb.EndInvoke (asyncResult);\r
+               }\r
+\r
+               public override Stream GetRequestStream()\r
+               {\r
+                       IAsyncResult asyncResult = BeginGetRequestStream (null, null);\r
+                       if (!(asyncResult.AsyncWaitHandle.WaitOne (timeout, false))) {\r
+                               throw new WebException("The request timed out", WebExceptionStatus.Timeout);\r
+                       }\r
+                       return EndGetRequestStream (asyncResult);\r
+               }\r
+               \r
+               internal Stream GetRequestStreamInternal ()\r
+               {\r
+                       this.requestStream = new FileWebStream (\r
+                                               this,\r
+                                               FileMode.Create,\r
+                                               FileAccess.Write, \r
+                                               FileShare.Read);\r
+                       return this.requestStream;\r
+               }\r
+               \r
+               public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)\r
+               {\r
+                       lock (this) {\r
+                               if (asyncResponding)\r
+                                       throw new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");\r
+                               asyncResponding = true;\r
+                       }\r
+                       GetResponseCallback c = new GetResponseCallback (this.GetResponseInternal);\r
+                       return c.BeginInvoke (callback, state);\r
                }\r
                \r
-               [MonoTODO]\r
                public override WebResponse EndGetResponse (IAsyncResult asyncResult)\r
                {\r
-                       throw new NotImplementedException ();\r
+                       if (asyncResult == null)\r
+                               throw new ArgumentNullException ("asyncResult");\r
+                       if (!asyncResult.IsCompleted)\r
+                               asyncResult.AsyncWaitHandle.WaitOne ();\r
+                       AsyncResult async = (AsyncResult) asyncResult;\r
+                       GetResponseCallback cb = (GetResponseCallback) async.AsyncDelegate;\r
+                       FileWebResponse webResponse = (FileWebResponse) cb.EndInvoke(asyncResult);\r
+                       asyncResponding = false;\r
+                       if (webResponse.HasError)\r
+                               throw webResponse.Error;\r
+                       return webResponse;\r
                }\r
                \r
-               public override Stream GetRequestStream()\r
+               public override WebResponse GetResponse ()\r
                {\r
-                       if (method == null || (!method.Equals ("PUT") && !method.Equals ("POST")))\r
-                               throw new ProtocolViolationException ("Cannot send file when method is: " + this.method + ". Method must be PUT.");\r
-                       if (open)\r
-                               throw new WebException ("Stream already open");\r
-                       open = true;\r
-                       FileStream fileStream = new FileWebStream (\r
-                                                       this,\r
-                                                       FileMode.CreateNew, \r
-                                                       FileAccess.Write, \r
-                                                       FileShare.Read,\r
-                                                       4096,\r
-                                                       false);\r
-                       return fileStream;                                      \r
-               }\r
-               \r
-               public override WebResponse GetResponse()\r
-               {\r
-                       if (method == null || !method.Equals ("GET"))\r
-                               throw new ProtocolViolationException ("Cannot retrieve file when method is: " + this.method + ". Method must be GET.");\r
-                       if (open)\r
-                               throw new WebException ("Stream already open");\r
-                       FileStream fileStream = new FileWebStream (\r
-                                                       this,\r
-                                                       FileMode.Open, \r
-                                                       FileAccess.Read, \r
-                                                       FileShare.Read,\r
-                                                       4096,\r
-                                                       false);\r
-                       return new FileWebResponse (this, fileStream);\r
+                       IAsyncResult asyncResult = BeginGetResponse (null, null);\r
+                       if (!(asyncResult.AsyncWaitHandle.WaitOne (timeout, false))) {\r
+                               throw new WebException("The request timed out", WebExceptionStatus.Timeout);\r
+                       }\r
+                       return EndGetResponse (asyncResult);\r
                }\r
                \r
-               [MonoTODO]\r
-               void ISerializable.GetObjectData (SerializationInfo serializationInfo,\r
-                                                 StreamingContext streamingContext)\r
+               WebResponse GetResponseInternal ()\r
                {\r
-                       throw new NotImplementedException ();\r
+                       if (webResponse != null)\r
+                               return webResponse;\r
+                       lock (this) {\r
+                               if (requesting) {\r
+                                       requestEndEvent = new AutoResetEvent (false);\r
+                               }\r
+                       }\r
+                       if (requestEndEvent != null) {\r
+                               requestEndEvent.WaitOne ();\r
+                       }\r
+                       FileStream fileStream = null;\r
+                       try {\r
+                               fileStream = new FileWebStream (this, FileMode.Open, FileAccess.Read, FileShare.Read);\r
+                               this.webResponse = new FileWebResponse (this.uri, fileStream);\r
+                       } catch (Exception ex) {\r
+                               this.webResponse = new FileWebResponse (this.uri, new WebException (ex.Message, ex));\r
+                       }\r
+                       return this.webResponse;\r
                }\r
                \r
-               internal void Close ()\r
+               void ISerializable.GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext)\r
                {\r
-                       open = false;\r
+                       GetObjectData (serializationInfo, streamingContext);\r
+               }\r
+\r
+               protected override void GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext)\r
+               {\r
+                       SerializationInfo info = serializationInfo;\r
+                       info.AddValue ("headers", webHeaders, typeof (WebHeaderCollection));\r
+                       info.AddValue ("proxy", proxy, typeof (IWebProxy));\r
+                       info.AddValue ("uri", uri, typeof (Uri));\r
+                       info.AddValue ("connectionGroupName", connectionGroup);\r
+                       info.AddValue ("method", method);\r
+                       info.AddValue ("contentLength", contentLength);\r
+                       info.AddValue ("timeout", timeout);\r
+                       info.AddValue ("fileAccess", fileAccess);\r
+                       info.AddValue ("preauthenticate", false);\r
                }\r
                \r
-               internal bool IsClosed ()\r
+               internal void Close ()\r
                {\r
-                       return !open;\r
+                       // already done in class below\r
+                       // if (requestStream != null) {\r
+                       //      requestStream.Close ();\r
+                       // }\r
+\r
+                       lock (this) {\r
+                               requesting = false;\r
+                               if (requestEndEvent != null) \r
+                                       requestEndEvent.Set ();\r
+                               // requestEndEvent = null;\r
+                       }\r
                }\r
                \r
                // to catch the Close called on the FileStream\r
@@ -192,20 +322,21 @@ namespace System.Net
                        internal FileWebStream (FileWebRequest webRequest,    \r
                                                FileMode mode,\r
                                                FileAccess access,\r
-                                               FileShare share,\r
-                                               int bufferSize,\r
-                                               bool useAsync)\r
+                                               FileShare share)\r
                                : base (webRequest.RequestUri.LocalPath, \r
-                                       mode, access, share, bufferSize, useAsync)                                              \r
+                                       mode, access, share)\r
                        {\r
                                this.webRequest = webRequest;\r
                        }\r
-                                               \r
+                       \r
                        public override void Close() \r
                        {\r
                                base.Close ();\r
-                               webRequest.Close ();\r
+                               FileWebRequest req = webRequest;\r
+                               webRequest = null;\r
+                               if (req != null)\r
+                                       req.Close ();\r
                        }\r
                }\r
        }\r
-}
\ No newline at end of file
+}\r