Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / System / System.Net / FileWebRequest.cs
1 //\r
2 // System.Net.FileWebRequest\r
3 //\r
4 // Author:\r
5 //   Lawrence Pit (loz@cable.a2000.nl)\r
6 //\r
7 \r
8 //\r
9 // Permission is hereby granted, free of charge, to any person obtaining\r
10 // a copy of this software and associated documentation files (the\r
11 // "Software"), to deal in the Software without restriction, including\r
12 // without limitation the rights to use, copy, modify, merge, publish,\r
13 // distribute, sublicense, and/or sell copies of the Software, and to\r
14 // permit persons to whom the Software is furnished to do so, subject to\r
15 // the following conditions:\r
16 // \r
17 // The above copyright notice and this permission notice shall be\r
18 // included in all copies or substantial portions of the Software.\r
19 // \r
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
27 //\r
28 \r
29 using System;\r
30 using System.Collections;\r
31 using System.IO;\r
32 using System.Runtime.Serialization;\r
33 using System.Runtime.Remoting.Messaging;\r
34 using System.Threading;\r
35 \r
36 namespace System.Net \r
37 {\r
38         [Serializable]\r
39         public class FileWebRequest : WebRequest, ISerializable\r
40         {\r
41                 private Uri uri;\r
42                 private WebHeaderCollection webHeaders;\r
43                 \r
44                 private ICredentials credentials;\r
45                 private string connectionGroup;\r
46                 private long contentLength;\r
47                 private FileAccess fileAccess = FileAccess.Read;\r
48                 private string method = "GET";\r
49                 private IWebProxy proxy;\r
50                 private bool preAuthenticate;\r
51                 private int timeout = 100000;\r
52                 \r
53                 private Stream requestStream;\r
54                 private FileWebResponse webResponse;\r
55                 private AutoResetEvent requestEndEvent;\r
56                 private bool requesting;\r
57                 private bool asyncResponding;\r
58                 \r
59                 // Constructors\r
60                 \r
61                 internal FileWebRequest (Uri uri) \r
62                 { \r
63                         this.uri = uri;\r
64                         this.webHeaders = new WebHeaderCollection ();\r
65                 }\r
66                 \r
67                 [Obsolete ("Serialization is obsoleted for this type", false)]\r
68                 protected FileWebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext) \r
69                 {\r
70                         SerializationInfo info = serializationInfo;\r
71                         webHeaders = (WebHeaderCollection) info.GetValue ("headers", typeof (WebHeaderCollection));\r
72                         proxy = (IWebProxy) info.GetValue ("proxy", typeof (IWebProxy));\r
73                         uri = (Uri) info.GetValue ("uri", typeof (Uri));\r
74                         connectionGroup = info.GetString ("connectionGroupName");\r
75                         method = info.GetString ("method");\r
76                         contentLength = info.GetInt64 ("contentLength");\r
77                         timeout = info.GetInt32 ("timeout");\r
78                         fileAccess = (FileAccess) info.GetValue ("fileAccess", typeof (FileAccess));\r
79                         preAuthenticate = info.GetBoolean ("preauthenticate");\r
80                 }\r
81                 \r
82                 // Properties\r
83                 \r
84                 // currently not used according to spec\r
85                 public override string ConnectionGroupName {\r
86                         get { return connectionGroup; }\r
87                         set { connectionGroup = value; }\r
88                 }\r
89                 \r
90                 public override long ContentLength {\r
91                         get { return contentLength; }\r
92                         set {\r
93                                 if (value < 0)\r
94                                         throw new ArgumentException ("The Content-Length value must be greater than or equal to zero.", "value");\r
95                                 contentLength =  value;\r
96                         }\r
97                 }\r
98                 \r
99                 public override string ContentType { \r
100                         get { return webHeaders ["Content-Type"]; }\r
101                         set { webHeaders ["Content-Type"] = value; }\r
102                 }\r
103                 \r
104                 public override ICredentials Credentials { \r
105                         get { return credentials; }\r
106                         set { credentials = value; }\r
107                 }\r
108                 \r
109                 public override WebHeaderCollection Headers { \r
110                         get { return webHeaders; }\r
111                 }\r
112                 \r
113                 // currently not used according to spec\r
114                 public override string Method { \r
115                         get { return this.method; }\r
116                         set {\r
117                                 if (value == null || value.Length == 0)\r
118                                         throw new ArgumentException ("Cannot set null or blank "\r
119                                                 + "methods on request.", "value");\r
120                                 this.method = value;\r
121                         }\r
122                 }\r
123                 \r
124                 // currently not used according to spec\r
125                 public override bool PreAuthenticate { \r
126                         get { return preAuthenticate; }\r
127                         set { preAuthenticate = value; }\r
128                 }\r
129                 \r
130                 // currently not used according to spec\r
131                 public override IWebProxy Proxy {\r
132                         get { return proxy; }\r
133                         set { proxy = value; }\r
134                 }\r
135                 \r
136                 public override Uri RequestUri { \r
137                         get { return this.uri; }\r
138                 }\r
139                 \r
140                 public override int Timeout { \r
141                         get { return timeout; }\r
142                         set { \r
143                                 if (value < -1)\r
144                                         throw new ArgumentOutOfRangeException ("Timeout can be "\r
145                                                 + "only set to 'System.Threading.Timeout.Infinite' "\r
146                                                 + "or a value >= 0.");\r
147                                 timeout = value;\r
148                         }\r
149                 }\r
150 \r
151                 public override bool UseDefaultCredentials\r
152                 {\r
153                         get {\r
154                                 throw new NotSupportedException ();\r
155                         }\r
156                         set {\r
157                                 throw new NotSupportedException ();\r
158                         }\r
159                 }\r
160                 // Methods\r
161                 \r
162                 private delegate Stream GetRequestStreamCallback ();\r
163                 private delegate WebResponse GetResponseCallback ();\r
164 \r
165                 static Exception GetMustImplement ()\r
166                 {\r
167                         return new NotImplementedException ();\r
168                 }\r
169                 \r
170                 /* LAMESPEC: Docs suggest this was present in 1.1 and\r
171                  * 1.0 profiles, but the masterinfos say otherwise\r
172                  */\r
173                 [MonoTODO]\r
174                 public override void Abort ()\r
175                 {\r
176                         throw GetMustImplement ();\r
177                 }\r
178 \r
179                 public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state) \r
180                 {\r
181                         if (string.Compare ("GET", method, true) == 0 ||\r
182                                 string.Compare ("HEAD", method, true) == 0 ||\r
183                                 string.Compare ("CONNECT", method, true) == 0)\r
184                                 throw new ProtocolViolationException ("Cannot send a content-body with this verb-type.");\r
185                         lock (this) {\r
186                                 if (asyncResponding || webResponse != null)\r
187                                         throw new InvalidOperationException ("This operation cannot be performed after the request has been submitted.");\r
188                                 if (requesting)\r
189                                         throw new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");\r
190                                 requesting = true;\r
191                         }\r
192                         GetRequestStreamCallback c = new GetRequestStreamCallback (this.GetRequestStreamInternal);\r
193                         return c.BeginInvoke (callback, state);\r
194                 }\r
195                 \r
196                 public override Stream EndGetRequestStream (IAsyncResult asyncResult)\r
197                 {\r
198                         if (asyncResult == null)\r
199                                 throw new ArgumentNullException ("asyncResult");\r
200                         if (!asyncResult.IsCompleted)\r
201                                 asyncResult.AsyncWaitHandle.WaitOne ();\r
202                         AsyncResult async = (AsyncResult) asyncResult;\r
203                         GetRequestStreamCallback cb = (GetRequestStreamCallback) async.AsyncDelegate;\r
204                         return cb.EndInvoke (asyncResult);\r
205                 }\r
206 \r
207                 public override Stream GetRequestStream()\r
208                 {\r
209                         IAsyncResult asyncResult = BeginGetRequestStream (null, null);\r
210                         if (!(asyncResult.AsyncWaitHandle.WaitOne (timeout, false))) {\r
211                                 throw new WebException("The request timed out", WebExceptionStatus.Timeout);\r
212                         }\r
213                         return EndGetRequestStream (asyncResult);\r
214                 }\r
215                 \r
216                 internal Stream GetRequestStreamInternal ()\r
217                 {\r
218                         this.requestStream = new FileWebStream (\r
219                                                 this,\r
220                                                 FileMode.Create,\r
221                                                 FileAccess.Write, \r
222                                                 FileShare.Read);\r
223                         return this.requestStream;\r
224                 }\r
225                 \r
226                 public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)\r
227                 {\r
228                         lock (this) {\r
229                                 if (asyncResponding)\r
230                                         throw new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");\r
231                                 asyncResponding = true;\r
232                         }\r
233                         GetResponseCallback c = new GetResponseCallback (this.GetResponseInternal);\r
234                         return c.BeginInvoke (callback, state);\r
235                 }\r
236                 \r
237                 public override WebResponse EndGetResponse (IAsyncResult asyncResult)\r
238                 {\r
239                         if (asyncResult == null)\r
240                                 throw new ArgumentNullException ("asyncResult");\r
241                         if (!asyncResult.IsCompleted)\r
242                                 asyncResult.AsyncWaitHandle.WaitOne ();\r
243                         AsyncResult async = (AsyncResult) asyncResult;\r
244                         GetResponseCallback cb = (GetResponseCallback) async.AsyncDelegate;\r
245                         FileWebResponse webResponse = (FileWebResponse) cb.EndInvoke(asyncResult);\r
246                         asyncResponding = false;\r
247                         if (webResponse.HasError)\r
248                                 throw webResponse.Error;\r
249                         return webResponse;\r
250                 }\r
251                 \r
252                 public override WebResponse GetResponse ()\r
253                 {\r
254                         IAsyncResult asyncResult = BeginGetResponse (null, null);\r
255                         if (!(asyncResult.AsyncWaitHandle.WaitOne (timeout, false))) {\r
256                                 throw new WebException("The request timed out", WebExceptionStatus.Timeout);\r
257                         }\r
258                         return EndGetResponse (asyncResult);\r
259                 }\r
260                 \r
261                 WebResponse GetResponseInternal ()\r
262                 {\r
263                         if (webResponse != null)\r
264                                 return webResponse;\r
265                         lock (this) {\r
266                                 if (requesting) {\r
267                                         requestEndEvent = new AutoResetEvent (false);\r
268                                 }\r
269                         }\r
270                         if (requestEndEvent != null) {\r
271                                 requestEndEvent.WaitOne ();\r
272                         }\r
273                         FileStream fileStream = null;\r
274                         try {\r
275                                 fileStream = new FileWebStream (this, FileMode.Open, FileAccess.Read, FileShare.Read);\r
276                                 this.webResponse = new FileWebResponse (this.uri, fileStream);\r
277                         } catch (Exception ex) {\r
278                                 this.webResponse = new FileWebResponse (this.uri, new WebException (ex.Message, ex));\r
279                         }\r
280                         return this.webResponse;\r
281                 }\r
282                 \r
283                 void ISerializable.GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext)\r
284                 {\r
285                         GetObjectData (serializationInfo, streamingContext);\r
286                 }\r
287 \r
288                 protected override void GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext)\r
289                 {\r
290                         SerializationInfo info = serializationInfo;\r
291                         info.AddValue ("headers", webHeaders, typeof (WebHeaderCollection));\r
292                         info.AddValue ("proxy", proxy, typeof (IWebProxy));\r
293                         info.AddValue ("uri", uri, typeof (Uri));\r
294                         info.AddValue ("connectionGroupName", connectionGroup);\r
295                         info.AddValue ("method", method);\r
296                         info.AddValue ("contentLength", contentLength);\r
297                         info.AddValue ("timeout", timeout);\r
298                         info.AddValue ("fileAccess", fileAccess);\r
299                         info.AddValue ("preauthenticate", false);\r
300                 }\r
301                 \r
302                 internal void Close ()\r
303                 {\r
304                         // already done in class below\r
305                         // if (requestStream != null) {\r
306                         //      requestStream.Close ();\r
307                         // }\r
308 \r
309                         lock (this) {\r
310                                 requesting = false;\r
311                                 if (requestEndEvent != null) \r
312                                         requestEndEvent.Set ();\r
313                                 // requestEndEvent = null;\r
314                         }\r
315                 }\r
316                 \r
317                 // to catch the Close called on the FileStream\r
318                 internal class FileWebStream : FileStream\r
319                 {\r
320                         FileWebRequest webRequest;\r
321                         \r
322                         internal FileWebStream (FileWebRequest webRequest,    \r
323                                                 FileMode mode,\r
324                                                 FileAccess access,\r
325                                                 FileShare share)\r
326                                 : base (webRequest.RequestUri.LocalPath, \r
327                                         mode, access, share)\r
328                         {\r
329                                 this.webRequest = webRequest;\r
330                         }\r
331                         \r
332                         public override void Close() \r
333                         {\r
334                                 base.Close ();\r
335                                 FileWebRequest req = webRequest;\r
336                                 webRequest = null;\r
337                                 if (req != null)\r
338                                         req.Close ();\r
339                         }\r
340                 }\r
341         }\r
342 }\r