Use lower-case names for Windows headers.
[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             bool success = true;
220             try {
221                 if (Aborted)
222                     throw ExceptionHelper.RequestAbortedException;
223                 if (!CanGetRequestStream()) {
224                     Exception e = new ProtocolViolationException(SR.GetString(SR.net_nouploadonget));
225                     GlobalLog.LeaveException("FileWebRequest::BeginGetRequestStream", e);
226                     throw e;
227                 }
228                 if (m_response != null) {
229                     Exception e = new InvalidOperationException(SR.GetString(SR.net_reqsubmitted));
230                     GlobalLog.LeaveException("FileWebRequest::BeginGetRequestStream", e);
231                     throw e;
232                 }
233                 lock(this) {
234                     if (m_writePending) {
235                         Exception e = new InvalidOperationException(SR.GetString(SR.net_repcall));
236                         GlobalLog.LeaveException("FileWebRequest::BeginGetRequestStream", e);
237                         throw e;
238                     }
239                     m_writePending = true;
240                 }
241                                                     
242                 //we need to force the capture of the identity and context to make sure the
243                 //posted callback doesn't inavertently gain access to something it shouldn't.
244                 m_ReadAResult = new LazyAsyncResult(this, state, callback);
245                 ThreadPool.QueueUserWorkItem(s_GetRequestStreamCallback, m_ReadAResult);
246             } catch (Exception exception) {
247                 success = false; 
248                 if(Logging.On)Logging.Exception(Logging.Web, this, "BeginGetRequestStream", exception);
249                 throw;
250             } finally {
251 #if !MONO
252                 if (FrameworkEventSource.Log.IsEnabled()) {
253                     LogBeginGetRequestStream(success, synchronous: false);
254                 }
255 #endif
256                 GlobalLog.Leave("FileWebRequest::BeginGetRequestSteam");
257             }
258
259             return m_ReadAResult;
260         }
261
262         [HostProtection(ExternalThreading=true)]
263         public override IAsyncResult BeginGetResponse(AsyncCallback callback, object state)
264         {
265             GlobalLog.Enter("FileWebRequest::BeginGetResponse");
266             bool success = true;
267
268             try {
269                 if (Aborted)
270                     throw ExceptionHelper.RequestAbortedException;
271                 lock(this) {
272                     if (m_readPending) {
273                         Exception e = new InvalidOperationException(SR.GetString(SR.net_repcall));
274                         GlobalLog.LeaveException("FileWebRequest::BeginGetResponse", e);
275                         throw e;
276                     }
277                     m_readPending = true;
278                 }
279
280                 m_WriteAResult = new LazyAsyncResult(this,state,callback);
281                 ThreadPool.QueueUserWorkItem(s_GetResponseCallback,m_WriteAResult);
282             } catch (Exception exception) {
283                 success = false;
284                 if(Logging.On)Logging.Exception(Logging.Web, this, "BeginGetResponse", exception);
285                 throw;
286             } finally {
287 #if !MONO
288                 if (FrameworkEventSource.Log.IsEnabled()) {
289                     LogBeginGetResponse(success, synchronous: false);
290                 }
291 #endif
292                 GlobalLog.Leave("FileWebRequest::BeginGetResponse");
293             }
294
295             return m_WriteAResult;
296         }
297
298         private bool CanGetRequestStream() {
299             return !KnownHttpVerb.Parse(m_method).ContentBodyNotAllowed;
300         }
301
302         public override Stream EndGetRequestStream(IAsyncResult asyncResult)
303         {
304             GlobalLog.Enter("FileWebRequest::EndGetRequestStream");
305
306             Stream stream;
307             bool success = false;
308             try {
309                 LazyAsyncResult  ar = asyncResult as LazyAsyncResult;
310                 if (asyncResult == null || ar == null) {
311                     Exception e = asyncResult == null? new ArgumentNullException("asyncResult"): new ArgumentException(SR.GetString(SR.InvalidAsyncResult), "asyncResult");
312                     GlobalLog.LeaveException("FileWebRequest::EndGetRequestStream", e);
313                     throw e;
314                 }
315
316                 object result = ar.InternalWaitForCompletion();
317                 if(result is Exception){
318                     throw (Exception)result;
319                 }
320                 stream = (Stream) result;
321                 m_writePending = false;
322                 success = true;
323             } catch (Exception exception) {
324                 if(Logging.On)Logging.Exception(Logging.Web, this, "EndGetRequestStream", exception);
325                 throw;
326             } finally {
327                 GlobalLog.Leave("FileWebRequest::EndGetRequestStream");
328 #if !MONO
329                 if (FrameworkEventSource.Log.IsEnabled()) {
330                     LogEndGetRequestStream(success, synchronous: false);
331                 }
332 #endif
333             }
334
335             return stream;
336         }
337
338         public override WebResponse EndGetResponse(IAsyncResult asyncResult)
339         {
340             GlobalLog.Enter("FileWebRequest::EndGetResponse");
341
342             WebResponse response;
343             bool success = false;
344             try {
345                 LazyAsyncResult  ar = asyncResult as LazyAsyncResult;
346                 if (asyncResult == null || ar == null) {
347                     Exception e = asyncResult == null? new ArgumentNullException("asyncResult"): new ArgumentException(SR.GetString(SR.InvalidAsyncResult), "asyncResult");
348                     GlobalLog.LeaveException("FileWebRequest::EndGetRequestStream", e);
349                     throw e;
350                 }
351
352
353                 object result = ar.InternalWaitForCompletion();
354                 if(result is Exception){
355                     throw (Exception)result;
356                 }
357                 response = (WebResponse) result;
358                 m_readPending = false;
359                 success = true;
360             } catch (Exception exception) {
361                 if(Logging.On)Logging.Exception(Logging.Web, this, "EndGetResponse", exception);
362                 throw;
363             } finally {
364                 GlobalLog.Leave("FileWebRequest::EndGetResponse");
365
366 #if !MONO
367                 // there is no statusCode in FileWebRequest object, defaulting it to zero.
368                 if (FrameworkEventSource.Log.IsEnabled()) {
369                     LogEndGetResponse(success, synchronous: false, statusCode: 0);
370                 }
371 #endif
372             }
373
374             return response;
375         }
376
377         public override Stream GetRequestStream()
378         {
379             GlobalLog.Enter("FileWebRequest::GetRequestStream");
380
381             IAsyncResult result;
382
383             try {
384                 result = BeginGetRequestStream(null, null);
385
386                 if ((Timeout != System.Threading.Timeout.Infinite) && !result.IsCompleted) {
387                     if (!result.AsyncWaitHandle.WaitOne(Timeout, false) || !result.IsCompleted) {
388                         if (m_stream != null) {
389                             m_stream.Close();
390                         }
391                         Exception e = new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout);
392                         GlobalLog.LeaveException("FileWebRequest::GetRequestStream", e);
393                         throw e;
394                     }
395                 }
396             } catch (Exception exception) {
397                 if(Logging.On)Logging.Exception(Logging.Web, this, "GetRequestStream", exception);
398                 throw;
399             } finally {
400                 GlobalLog.Leave("FileWebRequest::GetRequestStream");
401             }
402             return EndGetRequestStream(result);
403         }
404
405         public override WebResponse GetResponse() {
406             GlobalLog.Enter("FileWebRequest::GetResponse");
407
408             m_syncHint = true;
409
410             IAsyncResult result;
411
412             try {
413                 result = BeginGetResponse(null, null);
414
415                 if ((Timeout != System.Threading.Timeout.Infinite) && !result.IsCompleted) {
416                     if (!result.AsyncWaitHandle.WaitOne(Timeout, false) || !result.IsCompleted) {
417                         if (m_response != null) {
418                             m_response.Close();
419                         }
420                         Exception e = new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout);
421                         GlobalLog.LeaveException("FileWebRequest::GetResponse", e);
422                         throw e;
423                     }
424                 }
425             } catch (Exception exception) {
426                 if(Logging.On)Logging.Exception(Logging.Web, this, "GetResponse", exception);
427                 throw;
428             } finally {
429                 GlobalLog.Leave("FileWebRequest::GetResponse");
430             }
431             return EndGetResponse(result);
432         }
433
434         private static void GetRequestStreamCallback(object state)
435         {
436             GlobalLog.Enter("FileWebRequest::GetRequestStreamCallback");
437             LazyAsyncResult asyncResult = (LazyAsyncResult) state;
438             FileWebRequest request = (FileWebRequest)asyncResult.AsyncObject;
439
440             try
441             {
442                 if (request.m_stream == null)
443                 {
444                     request.m_stream = new FileWebStream(request, request.m_uri.LocalPath, FileMode.Create, FileAccess.Write, FileShare.Read);
445                     request.m_fileAccess = FileAccess.Write;
446                     request.m_writing = true;
447                 }
448             }
449             catch (Exception e)
450             {
451                 // any exceptions previously thrown must be passed to the callback
452                 Exception ex = new WebException(e.Message, e);
453                 GlobalLog.LeaveException("FileWebRequest::GetRequestStreamCallback", ex);
454
455                 // if the callback throws, correct behavior is to crash the process
456                 asyncResult.InvokeCallback(ex);
457                 return;
458             }
459
460             // if the callback throws, correct behavior is to crash the process
461             asyncResult.InvokeCallback(request.m_stream);
462             GlobalLog.Leave("FileWebRequest::GetRequestStreamCallback");
463         }
464
465         private static void GetResponseCallback(object state)
466         {
467             GlobalLog.Enter("FileWebRequest::GetResponseCallback");
468             LazyAsyncResult asyncResult = (LazyAsyncResult) state;
469             FileWebRequest request = (FileWebRequest)asyncResult.AsyncObject;
470
471             if (request.m_writePending || request.m_writing) {
472                 lock(request) {
473                     if (request.m_writePending || request.m_writing) {
474                         request.m_readerEvent = new ManualResetEvent(false);
475                     }
476                 }
477             }
478             if (request.m_readerEvent != null)
479                 request.m_readerEvent.WaitOne();
480
481             try
482             {
483                 if (request.m_response == null)
484                     request.m_response = new FileWebResponse(request, request.m_uri, request.m_fileAccess, !request.m_syncHint);               
485             }
486             catch (Exception e)
487             {
488                 // any exceptions previously thrown must be passed to the callback
489                 Exception ex = new WebException(e.Message, e);
490                 GlobalLog.LeaveException("FileWebRequest::GetResponseCallback", ex);
491
492                 // if the callback throws, correct behavior is to crash the process
493                 asyncResult.InvokeCallback(ex);
494                 return;
495             }
496
497             // if the callback throws, the correct behavior is to crash the process
498             asyncResult.InvokeCallback(request.m_response);
499             GlobalLog.Leave("FileWebRequest::GetResponseCallback");
500         }
501
502         internal void UnblockReader() {
503             GlobalLog.Enter("FileWebRequest::UnblockReader");
504             lock(this) {
505                 if (m_readerEvent != null) {
506                     m_readerEvent.Set();
507                 }
508             }
509             m_writing = false;
510             GlobalLog.Leave("FileWebRequest::UnblockReader");
511         }
512
513         // NOT SUPPORTED method
514         public override bool UseDefaultCredentials  {
515             get {
516                 throw ExceptionHelper.PropertyNotSupportedException;
517             }
518             set {
519                 throw ExceptionHelper.PropertyNotSupportedException;
520             }
521         }
522
523         public override void Abort()
524         {
525             GlobalLog.Enter("FileWebRequest::Abort");
526             if(Logging.On)Logging.PrintWarning(Logging.Web, NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled));
527             try {
528                 if (Interlocked.Increment(ref m_Aborted) == 1)
529                 {
530                     LazyAsyncResult readAResult = m_ReadAResult;
531                     LazyAsyncResult writeAResult = m_WriteAResult;
532
533                     WebException webException = new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled);
534
535                     Stream requestStream = m_stream;
536
537                     if (readAResult != null && !readAResult.IsCompleted)
538                         readAResult.InvokeCallback(webException);
539                     if (writeAResult != null && !writeAResult.IsCompleted)
540                         writeAResult.InvokeCallback(webException);
541
542                     if (requestStream != null)
543                         if (requestStream is ICloseEx)
544                             ((ICloseEx)requestStream).CloseEx(CloseExState.Abort);
545                         else
546                             requestStream.Close();
547
548                     if (m_response != null)
549                         ((ICloseEx)m_response).CloseEx(CloseExState.Abort);
550                 }
551             } catch (Exception exception) {
552                 if(Logging.On)Logging.Exception(Logging.Web, this, "Abort", exception);
553                 throw;
554             } finally {
555                 GlobalLog.Leave("FileWebRequest::Abort");
556             }
557         }
558     }
559
560     internal class FileWebRequestCreator : IWebRequestCreate {
561
562         internal FileWebRequestCreator() {
563         }
564
565         public WebRequest Create(Uri uri) {
566             return new FileWebRequest(uri);
567         }
568     }
569
570     internal sealed class FileWebStream : FileStream, ICloseEx {
571
572         FileWebRequest m_request;
573
574         [ResourceExposure(ResourceScope.Machine)]
575         [ResourceConsumption(ResourceScope.Machine)]
576         public FileWebStream(FileWebRequest request, string path, FileMode mode, FileAccess access, FileShare sharing)
577                  : base(path, mode, access, sharing)
578         {
579             GlobalLog.Enter("FileWebStream::FileWebStream");
580             m_request = request;
581             GlobalLog.Leave("FileWebStream::FileWebStream");
582         }
583
584         [ResourceExposure(ResourceScope.Machine)]
585         [ResourceConsumption(ResourceScope.Machine)]
586         public FileWebStream(FileWebRequest request, string path, FileMode mode, FileAccess access, FileShare sharing, int length, bool async)
587                 : base(path, mode, access, sharing, length, async)
588         {
589             GlobalLog.Enter("FileWebStream::FileWebStream");
590             m_request = request;
591             GlobalLog.Leave("FileWebStream::FileWebStream");
592         }
593
594         protected override void Dispose(bool disposing) {
595             GlobalLog.Enter("FileWebStream::Close");
596             try {
597                 if (disposing && m_request != null) {
598                     m_request.UnblockReader();
599                 }
600             }
601             finally {
602                 base.Dispose(disposing);
603             }
604             GlobalLog.Leave("FileWebStream::Close");
605         }
606
607         void ICloseEx.CloseEx(CloseExState closeState) {
608             if ((closeState & CloseExState.Abort) != 0)
609                 SafeFileHandle.Close();
610             else
611                 Close();
612         }
613
614         public override int Read(byte[] buffer, int offset, int size) {
615             CheckError();
616             try {
617                 return base.Read(buffer, offset, size);
618             }
619             catch {
620                 CheckError();
621                 throw;
622             }
623         }
624
625         public override void Write(byte[] buffer, int offset, int size) {
626             CheckError();
627             try {
628                 base.Write(buffer, offset, size);
629             }
630             catch {
631                 CheckError();
632                 throw;
633             }
634         }
635
636         public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, Object state) {
637             CheckError();
638             try {
639                 return base.BeginRead(buffer, offset, size, callback, state);
640             } 
641             catch {
642                 CheckError();
643                 throw;
644             }
645         }
646
647         public override int EndRead(IAsyncResult ar) {
648             try {
649                 return base.EndRead(ar);
650             }
651             catch {
652                 CheckError();
653                 throw;
654             }
655         }
656
657         public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, Object state) {
658             CheckError();
659             try {
660                 return base.BeginWrite(buffer, offset, size, callback, state);
661             } 
662             catch {
663                 CheckError();
664                 throw;
665             }
666         }
667
668         public override void EndWrite(IAsyncResult ar) {
669             try {
670                 base.EndWrite(ar);
671             }
672             catch {
673                 CheckError();
674                 throw;
675             }
676         }
677
678         private void CheckError() {
679             if (m_request.Aborted) {
680                 throw new WebException(
681                               NetRes.GetWebStatusString(
682                                   "net_requestaborted", 
683                                   WebExceptionStatus.RequestCanceled),
684                               WebExceptionStatus.RequestCanceled);
685             }    
686         }
687     }
688 }