Merge pull request #3800 from madewokherd/mingwbuild
[mono.git] / mcs / class / referencesource / System / net / System / Net / filewebrequest.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="filewebrequest.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net {
8     using System.IO;
9     using System.Runtime.Serialization;
10     using System.Security.Permissions;
11     using System.Threading;
12     using System.Runtime.Versioning;
13     using System.Diagnostics.CodeAnalysis;
14     using System.Diagnostics.Tracing;
15
16     [Serializable]
17     public class FileWebRequest : WebRequest, ISerializable {
18
19         private static WaitCallback s_GetRequestStreamCallback = new WaitCallback(GetRequestStreamCallback);
20         private static WaitCallback s_GetResponseCallback = new WaitCallback(GetResponseCallback);
21 #if !MONO
22         private static ContextCallback s_WrappedGetRequestStreamCallback = new ContextCallback(GetRequestStreamCallback);
23         private static ContextCallback s_WrappedResponseCallback = new ContextCallback(GetResponseCallback);
24 #endif
25
26     // fields
27
28         string m_connectionGroupName;
29         long m_contentLength;
30         ICredentials m_credentials;
31         FileAccess m_fileAccess;
32         WebHeaderCollection m_headers;
33         string m_method = "GET";
34         bool m_preauthenticate;
35         IWebProxy m_proxy;
36         ManualResetEvent m_readerEvent;
37         bool m_readPending;
38         WebResponse m_response;
39         Stream m_stream;
40         bool m_syncHint;
41         int m_timeout = WebRequest.DefaultTimeout;
42         Uri m_uri;
43         bool m_writePending;
44         bool m_writing;
45         private LazyAsyncResult m_WriteAResult;
46         private LazyAsyncResult m_ReadAResult;
47         private int                m_Aborted;
48
49     // constructors
50
51          internal FileWebRequest(Uri uri)
52          {
53              if ((object)uri.Scheme != (object)Uri.UriSchemeFile)
54                  throw new ArgumentOutOfRangeException("uri");
55
56             m_uri = uri;
57             m_fileAccess = FileAccess.Read;
58             m_headers = new WebHeaderCollection(WebHeaderCollectionType.FileWebRequest);
59         }
60
61
62         //
63         // ISerializable constructor
64         //
65
66         [Obsolete("Serialization is obsoleted for this type. http://go.microsoft.com/fwlink/?linkid=14202")]
67         protected FileWebRequest(SerializationInfo serializationInfo, StreamingContext streamingContext):base(serializationInfo, streamingContext) {
68             m_headers               = (WebHeaderCollection)serializationInfo.GetValue("headers", typeof(WebHeaderCollection));
69             m_proxy                 = (IWebProxy)serializationInfo.GetValue("proxy", typeof(IWebProxy));
70             m_uri                   = (Uri)serializationInfo.GetValue("uri", typeof(Uri));
71             m_connectionGroupName   = serializationInfo.GetString("connectionGroupName");
72             m_method                = serializationInfo.GetString("method");
73             m_contentLength         = serializationInfo.GetInt64("contentLength");
74             m_timeout               = serializationInfo.GetInt32("timeout");
75             m_fileAccess            = (FileAccess )serializationInfo.GetInt32("fileAccess");
76         }
77
78         //
79         // ISerializable method
80         //
81         /// <internalonly/>
82         [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")]
83         [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter, SerializationFormatter=true)]
84         void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
85         {
86             GetObjectData(serializationInfo, streamingContext);
87         }
88
89         //
90         // FxCop: provide some way for derived classes to access GetObjectData even if the derived class
91         // explicitly re-inherits ISerializable.
92         //
93         [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)]
94         protected override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
95         {
96             serializationInfo.AddValue("headers", m_headers, typeof(WebHeaderCollection));
97             serializationInfo.AddValue("proxy", m_proxy, typeof(IWebProxy));
98             serializationInfo.AddValue("uri", m_uri, typeof(Uri));
99             serializationInfo.AddValue("connectionGroupName", m_connectionGroupName);
100             serializationInfo.AddValue("method", m_method);
101             serializationInfo.AddValue("contentLength", m_contentLength);
102             serializationInfo.AddValue("timeout", m_timeout);
103             serializationInfo.AddValue("fileAccess", m_fileAccess);
104
105             //we're leaving this for legacy.  V1.1 and V1.0 had this field in the serialization constructor
106             serializationInfo.AddValue("preauthenticate", false);
107             base.GetObjectData(serializationInfo, streamingContext);
108         }
109
110
111     // properties
112
113         internal bool Aborted {
114             get {
115                 return m_Aborted != 0;
116             }
117         }
118
119         public override string ConnectionGroupName {
120             get {
121                 return m_connectionGroupName;
122             }
123             set {
124                 m_connectionGroupName = value;
125             }
126         }
127
128         public override long ContentLength {
129             get {
130                 return m_contentLength;
131             }
132             set {
133                 if (value < 0) {
134                     throw new ArgumentException(SR.GetString(SR.net_clsmall), "value");
135                 }
136                 m_contentLength = value;
137             }
138         }
139
140         public override string ContentType {
141             get {
142                 return m_headers["Content-Type"];
143             }
144             set {
145                 m_headers["Content-Type"] = value;
146             }
147         }
148
149         public override ICredentials Credentials {
150             get {
151                 return m_credentials;
152             }
153             set {
154                 m_credentials = value;
155             }
156         }
157
158         public override WebHeaderCollection Headers {
159             get {
160                 return m_headers;
161             }
162         }
163
164         public override string Method {
165             get {
166                 return m_method;
167             }
168             set {
169                 if (ValidationHelper.IsBlankString(value)) {
170                     throw new ArgumentException(SR.GetString(SR.net_badmethod), "value");
171                 }
172                 m_method = value;
173             }
174         }
175
176         public override bool PreAuthenticate {
177             get {
178                 return m_preauthenticate;
179             }
180             set {
181                 m_preauthenticate = true;
182             }
183         }
184
185         public override IWebProxy Proxy {
186             get {
187                 return m_proxy;
188             }
189             set {
190                 m_proxy = value;
191             }
192         }
193
194         //UEUE changed default from infinite to 100 seconds
195         public override int Timeout {
196             get {
197                 return m_timeout;
198             }
199             set {
200                 if ((value < 0) && (value != System.Threading.Timeout.Infinite)) {
201                     throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_io_timeout_use_ge_zero));
202                 }
203                 m_timeout = value;
204             }
205         }
206
207         public override Uri RequestUri {
208             get {
209                 return m_uri;
210             }
211         }
212
213     // methods
214
215         [HostProtection(ExternalThreading=true)]
216         public override IAsyncResult BeginGetRequestStream(AsyncCallback callback, object state)
217         {
218             GlobalLog.Enter("FileWebRequest::BeginGetRequestStream");
219 #if !MONO
220             bool success = true;
221 #endif
222             try {
223                 if (Aborted)
224                     throw ExceptionHelper.RequestAbortedException;
225                 if (!CanGetRequestStream()) {
226                     Exception e = new ProtocolViolationException(SR.GetString(SR.net_nouploadonget));
227                     GlobalLog.LeaveException("FileWebRequest::BeginGetRequestStream", e);
228                     throw e;
229                 }
230                 if (m_response != null) {
231                     Exception e = new InvalidOperationException(SR.GetString(SR.net_reqsubmitted));
232                     GlobalLog.LeaveException("FileWebRequest::BeginGetRequestStream", e);
233                     throw e;
234                 }
235                 lock(this) {
236                     if (m_writePending) {
237                         Exception e = new InvalidOperationException(SR.GetString(SR.net_repcall));
238                         GlobalLog.LeaveException("FileWebRequest::BeginGetRequestStream", e);
239                         throw e;
240                     }
241                     m_writePending = true;
242                 }
243                                                     
244                 //we need to force the capture of the identity and context to make sure the
245                 //posted callback doesn't inavertently gain access to something it shouldn't.
246                 m_ReadAResult = new LazyAsyncResult(this, state, callback);
247                 ThreadPool.QueueUserWorkItem(s_GetRequestStreamCallback, m_ReadAResult);
248             } catch (Exception exception) {
249 #if !MONO
250                 success = false; 
251 #endif
252                 if(Logging.On)Logging.Exception(Logging.Web, this, "BeginGetRequestStream", exception);
253                 throw;
254             } finally {
255 #if !MONO
256                 if (FrameworkEventSource.Log.IsEnabled()) {
257                     LogBeginGetRequestStream(success, synchronous: false);
258                 }
259 #endif
260                 GlobalLog.Leave("FileWebRequest::BeginGetRequestSteam");
261             }
262
263             return m_ReadAResult;
264         }
265
266         [HostProtection(ExternalThreading=true)]
267         public override IAsyncResult BeginGetResponse(AsyncCallback callback, object state)
268         {
269             GlobalLog.Enter("FileWebRequest::BeginGetResponse");
270 #if !MONO
271             bool success = true;
272 #endif
273
274             try {
275                 if (Aborted)
276                     throw ExceptionHelper.RequestAbortedException;
277                 lock(this) {
278                     if (m_readPending) {
279                         Exception e = new InvalidOperationException(SR.GetString(SR.net_repcall));
280                         GlobalLog.LeaveException("FileWebRequest::BeginGetResponse", e);
281                         throw e;
282                     }
283                     m_readPending = true;
284                 }
285
286                 m_WriteAResult = new LazyAsyncResult(this,state,callback);
287                 ThreadPool.QueueUserWorkItem(s_GetResponseCallback,m_WriteAResult);
288             } catch (Exception exception) {
289 #if !MONO
290                 success = false;
291 #endif
292                 if(Logging.On)Logging.Exception(Logging.Web, this, "BeginGetResponse", exception);
293                 throw;
294             } finally {
295 #if !MONO
296                 if (FrameworkEventSource.Log.IsEnabled()) {
297                     LogBeginGetResponse(success, synchronous: false);
298                 }
299 #endif
300                 GlobalLog.Leave("FileWebRequest::BeginGetResponse");
301             }
302
303             return m_WriteAResult;
304         }
305
306         private bool CanGetRequestStream() {
307             return !KnownHttpVerb.Parse(m_method).ContentBodyNotAllowed;
308         }
309
310         public override Stream EndGetRequestStream(IAsyncResult asyncResult)
311         {
312             GlobalLog.Enter("FileWebRequest::EndGetRequestStream");
313
314             Stream stream;
315 #if !MONO
316             bool success = false;
317 #endif
318             try {
319                 LazyAsyncResult  ar = asyncResult as LazyAsyncResult;
320                 if (asyncResult == null || ar == null) {
321                     Exception e = asyncResult == null? new ArgumentNullException("asyncResult"): new ArgumentException(SR.GetString(SR.InvalidAsyncResult), "asyncResult");
322                     GlobalLog.LeaveException("FileWebRequest::EndGetRequestStream", e);
323                     throw e;
324                 }
325
326                 object result = ar.InternalWaitForCompletion();
327                 if(result is Exception){
328                     throw (Exception)result;
329                 }
330                 stream = (Stream) result;
331                 m_writePending = false;
332 #if !MONO
333                 success = true;
334 #endif
335             } catch (Exception exception) {
336                 if(Logging.On)Logging.Exception(Logging.Web, this, "EndGetRequestStream", exception);
337                 throw;
338             } finally {
339                 GlobalLog.Leave("FileWebRequest::EndGetRequestStream");
340 #if !MONO
341                 if (FrameworkEventSource.Log.IsEnabled()) {
342                     LogEndGetRequestStream(success, synchronous: false);
343                 }
344 #endif
345             }
346
347             return stream;
348         }
349
350         public override WebResponse EndGetResponse(IAsyncResult asyncResult)
351         {
352             GlobalLog.Enter("FileWebRequest::EndGetResponse");
353
354             WebResponse response;
355 #if !MONO
356             bool success = false;
357 #endif
358             try {
359                 LazyAsyncResult  ar = asyncResult as LazyAsyncResult;
360                 if (asyncResult == null || ar == null) {
361                     Exception e = asyncResult == null? new ArgumentNullException("asyncResult"): new ArgumentException(SR.GetString(SR.InvalidAsyncResult), "asyncResult");
362                     GlobalLog.LeaveException("FileWebRequest::EndGetRequestStream", e);
363                     throw e;
364                 }
365
366
367                 object result = ar.InternalWaitForCompletion();
368                 if(result is Exception){
369                     throw (Exception)result;
370                 }
371                 response = (WebResponse) result;
372                 m_readPending = false;
373 #if !MONO
374                 success = true;
375 #endif
376             } catch (Exception exception) {
377                 if(Logging.On)Logging.Exception(Logging.Web, this, "EndGetResponse", exception);
378                 throw;
379             } finally {
380                 GlobalLog.Leave("FileWebRequest::EndGetResponse");
381
382 #if !MONO
383                 // there is no statusCode in FileWebRequest object, defaulting it to zero.
384                 if (FrameworkEventSource.Log.IsEnabled()) {
385                     LogEndGetResponse(success, synchronous: false, statusCode: 0);
386                 }
387 #endif
388             }
389
390             return response;
391         }
392
393         public override Stream GetRequestStream()
394         {
395             GlobalLog.Enter("FileWebRequest::GetRequestStream");
396
397             IAsyncResult result;
398
399             try {
400                 result = BeginGetRequestStream(null, null);
401
402                 if ((Timeout != System.Threading.Timeout.Infinite) && !result.IsCompleted) {
403                     if (!result.AsyncWaitHandle.WaitOne(Timeout, false) || !result.IsCompleted) {
404                         if (m_stream != null) {
405                             m_stream.Close();
406                         }
407                         Exception e = new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout);
408                         GlobalLog.LeaveException("FileWebRequest::GetRequestStream", e);
409                         throw e;
410                     }
411                 }
412             } catch (Exception exception) {
413                 if(Logging.On)Logging.Exception(Logging.Web, this, "GetRequestStream", exception);
414                 throw;
415             } finally {
416                 GlobalLog.Leave("FileWebRequest::GetRequestStream");
417             }
418             return EndGetRequestStream(result);
419         }
420
421         public override WebResponse GetResponse() {
422             GlobalLog.Enter("FileWebRequest::GetResponse");
423
424             m_syncHint = true;
425
426             IAsyncResult result;
427
428             try {
429                 result = BeginGetResponse(null, null);
430
431                 if ((Timeout != System.Threading.Timeout.Infinite) && !result.IsCompleted) {
432                     if (!result.AsyncWaitHandle.WaitOne(Timeout, false) || !result.IsCompleted) {
433                         if (m_response != null) {
434                             m_response.Close();
435                         }
436                         Exception e = new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout);
437                         GlobalLog.LeaveException("FileWebRequest::GetResponse", e);
438                         throw e;
439                     }
440                 }
441             } catch (Exception exception) {
442                 if(Logging.On)Logging.Exception(Logging.Web, this, "GetResponse", exception);
443                 throw;
444             } finally {
445                 GlobalLog.Leave("FileWebRequest::GetResponse");
446             }
447             return EndGetResponse(result);
448         }
449
450         private static void GetRequestStreamCallback(object state)
451         {
452             GlobalLog.Enter("FileWebRequest::GetRequestStreamCallback");
453             LazyAsyncResult asyncResult = (LazyAsyncResult) state;
454             FileWebRequest request = (FileWebRequest)asyncResult.AsyncObject;
455
456             try
457             {
458                 if (request.m_stream == null)
459                 {
460                     request.m_stream = new FileWebStream(request, request.m_uri.LocalPath, FileMode.Create, FileAccess.Write, FileShare.Read);
461                     request.m_fileAccess = FileAccess.Write;
462                     request.m_writing = true;
463                 }
464             }
465             catch (Exception e)
466             {
467                 // any exceptions previously thrown must be passed to the callback
468                 Exception ex = new WebException(e.Message, e);
469                 GlobalLog.LeaveException("FileWebRequest::GetRequestStreamCallback", ex);
470
471                 // if the callback throws, correct behavior is to crash the process
472                 asyncResult.InvokeCallback(ex);
473                 return;
474             }
475
476             // if the callback throws, correct behavior is to crash the process
477             asyncResult.InvokeCallback(request.m_stream);
478             GlobalLog.Leave("FileWebRequest::GetRequestStreamCallback");
479         }
480
481         private static void GetResponseCallback(object state)
482         {
483             GlobalLog.Enter("FileWebRequest::GetResponseCallback");
484             LazyAsyncResult asyncResult = (LazyAsyncResult) state;
485             FileWebRequest request = (FileWebRequest)asyncResult.AsyncObject;
486
487             if (request.m_writePending || request.m_writing) {
488                 lock(request) {
489                     if (request.m_writePending || request.m_writing) {
490                         request.m_readerEvent = new ManualResetEvent(false);
491                     }
492                 }
493             }
494             if (request.m_readerEvent != null)
495                 request.m_readerEvent.WaitOne();
496
497             try
498             {
499                 if (request.m_response == null)
500                     request.m_response = new FileWebResponse(request, request.m_uri, request.m_fileAccess, !request.m_syncHint);               
501             }
502             catch (Exception e)
503             {
504                 // any exceptions previously thrown must be passed to the callback
505                 Exception ex = new WebException(e.Message, e);
506                 GlobalLog.LeaveException("FileWebRequest::GetResponseCallback", ex);
507
508                 // if the callback throws, correct behavior is to crash the process
509                 asyncResult.InvokeCallback(ex);
510                 return;
511             }
512
513             // if the callback throws, the correct behavior is to crash the process
514             asyncResult.InvokeCallback(request.m_response);
515             GlobalLog.Leave("FileWebRequest::GetResponseCallback");
516         }
517
518         internal void UnblockReader() {
519             GlobalLog.Enter("FileWebRequest::UnblockReader");
520             lock(this) {
521                 if (m_readerEvent != null) {
522                     m_readerEvent.Set();
523                 }
524             }
525             m_writing = false;
526             GlobalLog.Leave("FileWebRequest::UnblockReader");
527         }
528
529         // NOT SUPPORTED method
530         public override bool UseDefaultCredentials  {
531             get {
532                 throw ExceptionHelper.PropertyNotSupportedException;
533             }
534             set {
535                 throw ExceptionHelper.PropertyNotSupportedException;
536             }
537         }
538
539         public override void Abort()
540         {
541             GlobalLog.Enter("FileWebRequest::Abort");
542             if(Logging.On)Logging.PrintWarning(Logging.Web, NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled));
543             try {
544                 if (Interlocked.Increment(ref m_Aborted) == 1)
545                 {
546                     LazyAsyncResult readAResult = m_ReadAResult;
547                     LazyAsyncResult writeAResult = m_WriteAResult;
548
549                     WebException webException = new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled);
550
551                     Stream requestStream = m_stream;
552
553                     if (readAResult != null && !readAResult.IsCompleted)
554                         readAResult.InvokeCallback(webException);
555                     if (writeAResult != null && !writeAResult.IsCompleted)
556                         writeAResult.InvokeCallback(webException);
557
558                     if (requestStream != null)
559                         if (requestStream is ICloseEx)
560                             ((ICloseEx)requestStream).CloseEx(CloseExState.Abort);
561                         else
562                             requestStream.Close();
563
564                     if (m_response != null)
565                         ((ICloseEx)m_response).CloseEx(CloseExState.Abort);
566                 }
567             } catch (Exception exception) {
568                 if(Logging.On)Logging.Exception(Logging.Web, this, "Abort", exception);
569                 throw;
570             } finally {
571                 GlobalLog.Leave("FileWebRequest::Abort");
572             }
573         }
574     }
575
576     internal class FileWebRequestCreator : IWebRequestCreate {
577
578         internal FileWebRequestCreator() {
579         }
580
581         public WebRequest Create(Uri uri) {
582             return new FileWebRequest(uri);
583         }
584     }
585
586     internal sealed class FileWebStream : FileStream, ICloseEx {
587
588         FileWebRequest m_request;
589
590         [ResourceExposure(ResourceScope.Machine)]
591         [ResourceConsumption(ResourceScope.Machine)]
592         public FileWebStream(FileWebRequest request, string path, FileMode mode, FileAccess access, FileShare sharing)
593                  : base(path, mode, access, sharing)
594         {
595             GlobalLog.Enter("FileWebStream::FileWebStream");
596             m_request = request;
597             GlobalLog.Leave("FileWebStream::FileWebStream");
598         }
599
600         [ResourceExposure(ResourceScope.Machine)]
601         [ResourceConsumption(ResourceScope.Machine)]
602         public FileWebStream(FileWebRequest request, string path, FileMode mode, FileAccess access, FileShare sharing, int length, bool async)
603                 : base(path, mode, access, sharing, length, async)
604         {
605             GlobalLog.Enter("FileWebStream::FileWebStream");
606             m_request = request;
607             GlobalLog.Leave("FileWebStream::FileWebStream");
608         }
609
610         protected override void Dispose(bool disposing) {
611             GlobalLog.Enter("FileWebStream::Close");
612             try {
613                 if (disposing && m_request != null) {
614                     m_request.UnblockReader();
615                 }
616             }
617             finally {
618                 base.Dispose(disposing);
619             }
620             GlobalLog.Leave("FileWebStream::Close");
621         }
622
623         void ICloseEx.CloseEx(CloseExState closeState) {
624             if ((closeState & CloseExState.Abort) != 0)
625                 SafeFileHandle.Close();
626             else
627                 Close();
628         }
629
630         public override int Read(byte[] buffer, int offset, int size) {
631             CheckError();
632             try {
633                 return base.Read(buffer, offset, size);
634             }
635             catch {
636                 CheckError();
637                 throw;
638             }
639         }
640
641         public override void Write(byte[] buffer, int offset, int size) {
642             CheckError();
643             try {
644                 base.Write(buffer, offset, size);
645             }
646             catch {
647                 CheckError();
648                 throw;
649             }
650         }
651
652         public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, Object state) {
653             CheckError();
654             try {
655                 return base.BeginRead(buffer, offset, size, callback, state);
656             } 
657             catch {
658                 CheckError();
659                 throw;
660             }
661         }
662
663         public override int EndRead(IAsyncResult ar) {
664             try {
665                 return base.EndRead(ar);
666             }
667             catch {
668                 CheckError();
669                 throw;
670             }
671         }
672
673         public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, Object state) {
674             CheckError();
675             try {
676                 return base.BeginWrite(buffer, offset, size, callback, state);
677             } 
678             catch {
679                 CheckError();
680                 throw;
681             }
682         }
683
684         public override void EndWrite(IAsyncResult ar) {
685             try {
686                 base.EndWrite(ar);
687             }
688             catch {
689                 CheckError();
690                 throw;
691             }
692         }
693
694         private void CheckError() {
695             if (m_request.Aborted) {
696                 throw new WebException(
697                               NetRes.GetWebStatusString(
698                                   "net_requestaborted", 
699                                   WebExceptionStatus.RequestCanceled),
700                               WebExceptionStatus.RequestCanceled);
701             }    
702         }
703     }
704 }