Unbreak the build
[mono.git] / mcs / class / System / System.Net / FileWebRequest.cs
index 5035331e08aee12b602c65395b533640584dd9ad..08c2d00c075b9294fe7e5872e8a5d11ddf619eda 100644 (file)
@@ -5,11 +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
@@ -21,46 +43,62 @@ 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
+#if NET_2_0\r
+               [Obsolete ("Serialization is obsoleted for this type", false)]\r
+#endif\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
+#if NET_2_0\r
+                                       throw new ArgumentException ("The Content-Length value must be greater than or equal to zero.", "value");\r
+#else\r
                                        throw new ArgumentException ("value");\r
-                               webHeaders ["Content-Length"] = Convert.ToString (value);\r
+#endif\r
+                               contentLength =  value;\r
                        }\r
                }\r
                \r
@@ -81,19 +119,29 @@ 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
+#if NET_2_0\r
+                                       throw new ArgumentException ("Cannot set null or blank "\r
+                                               + "methods on request.", "value");\r
+#else\r
+                                       throw new ArgumentException ("Cannot set null or blank "\r
+                                               + "methods on request.");\r
+#endif\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
@@ -103,106 +151,194 @@ 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
+#if NET_2_0\r
+                                       throw new ArgumentOutOfRangeException ("Timeout can be "\r
+                                               + "only set to 'System.Threading.Timeout.Infinite' "\r
+                                               + "or a value >= 0.");\r
+#else\r
+                                       throw new ArgumentOutOfRangeException ("value");\r
+#endif\r
                                timeout = value;\r
                        }\r
                }\r
+\r
+#if NET_2_0\r
+               public override bool UseDefaultCredentials\r
+               {\r
+                       get {\r
+                               throw new NotSupportedException ();\r
+                       }\r
+                       set {\r
+                               throw new NotSupportedException ();\r
+                       }\r
+               }\r
+#endif\r
                \r
                // Methods\r
                \r
                private delegate Stream GetRequestStreamCallback ();\r
                private delegate WebResponse GetResponseCallback ();\r
 \r
-// TODO: bit simplistic this, need to add code to check for exceptions..\r
-// TODO: use Timeout value\r
-// TODO: as the spec says GetResponse can throw an exception on timeout, \r
-// and because in theory one could start an async GetResponse, change \r
-// properties, and start a sync GetResponse it seems best that GetResponse \r
-// actually works via the async methods and the async delegates delegate \r
-// to a private get response method. timeout value probably isn't an \r
-// issue here, but it is in HttpWebRequest.\r
-\r
-               public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state) \r
-               {               \r
-                       GetRequestStreamCallback c = new GetRequestStreamCallback (this.GetRequestStream);\r
-                       return c.BeginInvoke (callback, state);\r
+#if NET_2_0\r
+               static Exception GetMustImplement ()\r
+               {\r
+                       return new NotImplementedException ();\r
                }\r
                \r
-               public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)\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 void Abort ()\r
                {\r
-                       GetResponseCallback c = new GetResponseCallback (this.GetResponse);\r
-                       return c.BeginInvoke (callback, state);\r
+                       throw GetMustImplement ();\r
                }\r
+#endif\r
 \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
                        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
-                       asyncResult.AsyncWaitHandle.WaitOne ();\r
-                       return cb.EndInvoke(asyncResult);\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
                public override WebResponse EndGetResponse (IAsyncResult asyncResult)\r
                {\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
-                       asyncResult.AsyncWaitHandle.WaitOne ();\r
-                       return cb.EndInvoke(asyncResult);               \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
+#if NET_2_0\r
+               protected override\r
+#endif\r
+               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
+#if NET_2_0\r
+                       info.AddValue ("preauthenticate", false);\r
+#else\r
+                       info.AddValue ("preauthenticate", preAuthenticate);\r
+#endif\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
@@ -213,20 +349,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