// // System.Net.FileWebRequest // // Author: // Lawrence Pit (loz@cable.a2000.nl) // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections; using System.IO; using System.Runtime.Serialization; using System.Runtime.Remoting.Messaging; using System.Threading; namespace System.Net { [Serializable] public class FileWebRequest : WebRequest, ISerializable { private Uri uri; private WebHeaderCollection webHeaders; private ICredentials credentials; private string connectionGroup; private string method = "GET"; private int timeout = 100000; private Stream requestStream; private FileWebResponse webResponse; private AutoResetEvent requestEndEvent; private bool requesting; private bool asyncResponding; // Constructors internal FileWebRequest (Uri uri) { this.uri = uri; this.webHeaders = new WebHeaderCollection (); } protected FileWebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext) { SerializationInfo info = serializationInfo; method = info.GetString ("method"); uri = (Uri) info.GetValue ("uri", typeof (Uri)); timeout = info.GetInt32 ("timeout"); connectionGroup = info.GetString ("connectionGroup"); webHeaders = (WebHeaderCollection) info.GetValue ("webHeaders", typeof (WebHeaderCollection)); } // Properties // currently not used according to spec public override string ConnectionGroupName { get { return connectionGroup; } set { connectionGroup = value; } } public override long ContentLength { get { try { return Int64.Parse (webHeaders ["Content-Length"]); } catch (Exception) { return 0; } } set { if (value < 0) throw new ArgumentException ("value"); webHeaders ["Content-Length"] = Convert.ToString (value); } } public override string ContentType { get { return webHeaders ["Content-Type"]; } set { webHeaders ["Content-Type"] = value; } } public override ICredentials Credentials { get { return credentials; } set { credentials = value; } } public override WebHeaderCollection Headers { get { return webHeaders; } } // currently not used according to spec public override string Method { get { return this.method; } set { this.method = value; } } // currently not used according to spec public override bool PreAuthenticate { get { throw new NotSupportedException (); } set { throw new NotSupportedException (); } } // currently not used according to spec public override IWebProxy Proxy { get { throw new NotSupportedException (); } set { throw new NotSupportedException (); } } public override Uri RequestUri { get { return this.uri; } } public override int Timeout { get { return timeout; } set { if (value < 0) throw new ArgumentException ("value"); timeout = value; } } // Methods private delegate Stream GetRequestStreamCallback (); private delegate WebResponse GetResponseCallback (); public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state) { if (method == null || (!method.Equals ("PUT") && !method.Equals ("POST"))) throw new ProtocolViolationException ("Cannot send file when method is: " + this.method + ". Method must be PUT."); // workaround for bug 24943 Exception e = null; lock (this) { if (asyncResponding || webResponse != null) e = new InvalidOperationException ("This operation cannot be performed after the request has been submitted."); else if (requesting) e = new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress."); else requesting = true; } if (e != null) throw e; /* lock (this) { if (asyncResponding || webResponse != null) throw new InvalidOperationException ("This operation cannot be performed after the request has been submitted."); if (requesting) throw new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress."); requesting = true; } */ GetRequestStreamCallback c = new GetRequestStreamCallback (this.GetRequestStreamInternal); return c.BeginInvoke (callback, state); } public override Stream EndGetRequestStream (IAsyncResult asyncResult) { if (asyncResult == null) throw new ArgumentNullException ("asyncResult"); if (!asyncResult.IsCompleted) asyncResult.AsyncWaitHandle.WaitOne (); AsyncResult async = (AsyncResult) asyncResult; GetRequestStreamCallback cb = (GetRequestStreamCallback) async.AsyncDelegate; return cb.EndInvoke (asyncResult); } public override Stream GetRequestStream() { IAsyncResult asyncResult = BeginGetRequestStream (null, null); if (!(asyncResult.AsyncWaitHandle.WaitOne (timeout, false))) { throw new WebException("The request timed out", WebExceptionStatus.Timeout); } return EndGetRequestStream (asyncResult); } internal Stream GetRequestStreamInternal () { this.requestStream = new FileWebStream ( this, FileMode.CreateNew, FileAccess.Write, FileShare.Read); return this.requestStream; } public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state) { // workaround for bug 24943 Exception e = null; lock (this) { if (asyncResponding) e = new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress."); else asyncResponding = true; } if (e != null) throw e; /* lock (this) { if (asyncResponding) throw new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress."); asyncResponding = true; } */ GetResponseCallback c = new GetResponseCallback (this.GetResponseInternal); return c.BeginInvoke (callback, state); } public override WebResponse EndGetResponse (IAsyncResult asyncResult) { if (asyncResult == null) throw new ArgumentNullException ("asyncResult"); if (!asyncResult.IsCompleted) asyncResult.AsyncWaitHandle.WaitOne (); AsyncResult async = (AsyncResult) asyncResult; GetResponseCallback cb = (GetResponseCallback) async.AsyncDelegate; WebResponse webResponse = cb.EndInvoke(asyncResult); asyncResponding = false; return webResponse; } public override WebResponse GetResponse () { IAsyncResult asyncResult = BeginGetResponse (null, null); if (!(asyncResult.AsyncWaitHandle.WaitOne (timeout, false))) { throw new WebException("The request timed out", WebExceptionStatus.Timeout); } return EndGetResponse (asyncResult); } WebResponse GetResponseInternal () { if (webResponse != null) return webResponse; lock (this) { if (requesting) { requestEndEvent = new AutoResetEvent (false); } } if (requestEndEvent != null) { requestEndEvent.WaitOne (); } FileStream fileStream = new FileWebStream ( this, FileMode.Open, FileAccess.Read, FileShare.Read); this.webResponse = new FileWebResponse (this.uri, fileStream); return (WebResponse) this.webResponse; } void ISerializable.GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext) { SerializationInfo info = serializationInfo; info.AddValue ("method", method); info.AddValue ("uri", uri, typeof (Uri)); info.AddValue ("timeout", timeout); info.AddValue ("connectionGroup", connectionGroup); info.AddValue ("webHeaders", webHeaders, typeof (WebHeaderCollection)); } internal void Close () { // already done in class below // if (requestStream != null) { // requestStream.Close (); // } lock (this) { requesting = false; if (requestEndEvent != null) requestEndEvent.Set (); // requestEndEvent = null; } } // to catch the Close called on the FileStream internal class FileWebStream : FileStream { FileWebRequest webRequest; internal FileWebStream (FileWebRequest webRequest, FileMode mode, FileAccess access, FileShare share) : base (webRequest.RequestUri.LocalPath, mode, access, share) { this.webRequest = webRequest; } public override void Close() { base.Close (); FileWebRequest req = webRequest; webRequest = null; if (req != null) req.Close (); } } } }