using System; using System.Diagnostics; using System.IO; using System.Net; using System.Text.RegularExpressions; using System.Threading; using AppLimit.CloudComputing.SharpBox.Common.IO; namespace AppLimit.CloudComputing.SharpBox.Common.Net.Web { internal abstract class WebRequestService { private const string watchTag = "WebRequestServiceRequestWatch"; #region StopWatch Helper private static void StopStopWatch(WebResponse response, Stream ret, WebException exception) { // 2. stop the watch from tls var slot = Thread.GetNamedDataSlot(watchTag); var watch = Thread.GetData(slot) as Stopwatch; if (watch != null) { // stop watch watch.Stop(); // notify WebRequestManager.Instance.NotifyWebRequestExecuted(response, watch.Elapsed, ret, exception); } } #endregion #region Base Web Methods /// /// This method generates a new webrequest and instruments the stopwatch /// counter /// /// /// /// /// /// /// public virtual WebRequest CreateWebRequest(string url, string method, ICredentials credentials, bool bAllowStreamBuffering, object context) { return CreateWebRequest(url, method, credentials, bAllowStreamBuffering, context, null); } /// /// The callback /// /// /// public delegate void CreateWebRequestPreparationCallback(WebRequest request, object context); /// /// Internal web request generator /// /// /// /// /// /// /// /// protected WebRequest CreateWebRequest(string url, string method, ICredentials credentials, bool bAllowStreamBuffering, object context, CreateWebRequestPreparationCallback callback) { // 1. create and start the watch var watch = new Stopwatch(); watch.Start(); // 2. build uri var uriNew = new Uri(url); // 2. create request var request = CreateBasicWebRequest(uriNew, bAllowStreamBuffering); request.Method = method.ToUpper(); // 3. call back if (callback != null) callback(request, context); // 4. set the proxy class if needed if (WebRequestManager.Instance.GetProxySettings() != null) request.Proxy = WebRequestManager.Instance.GetProxySettings(); // 4.1. check if we got a null proxy if (request.Proxy is WebRequestManagerNullProxy) request.Proxy = null; // 5. set the credentials if needed if (credentials != null) { request.Credentials = credentials; try { request.PreAuthenticate = true; } catch (NotSupportedException) { } } // 6. notify WebRequestManager.Instance.NotifyWebRequestPrepared(request); // 7. add watch object to tls var slot = Thread.GetNamedDataSlot(watchTag); Thread.SetData(slot, watch); // 8. return the request return request; } /// /// Please implement this method for the right WebRequest type /// /// /// /// protected abstract WebRequest CreateBasicWebRequest(Uri uri, bool bAllowStreamBuffering); /// /// This method returns a webrequest data stream and should be used in /// a using clause /// /// /// /// public virtual WebRequestStream GetRequestStream(WebRequest request, long length) { // set the conten length request.ContentLength = length; // get the network stream var nwStream = WebRequestStreamHelper.GetRequestStream(request); // return the request streaM; var ws = new WebRequestStream(nwStream, request, this); // add pre dispose opp ws.PushPreDisposeOperation(DisposeWebRequestStreams, request, ws); // go ahead return ws; } /// /// This method contains the routine which has to be executed when a request /// stream will be disposed and will be called autoamtically /// private static void DisposeWebRequestStreams(params object[] arg) { // get the params var request = arg[0] as WebRequest; var stream = arg[1] as WebRequestStream; // check if we have a multipart upload var md = new WebRequestMultipartFormDataSupport(); if (md.IsRequestMultiPartUpload(request)) md.FinalizeNetworkFileDataStream(stream); } /// /// This method returns a webresponse or throws an exception. In the case of an /// exception the stop watch is stop here /// /// /// public virtual WebResponse GetWebResponse(WebRequest request) { // execute the webrequest try { // check the length if (request.ContentLength == -1) { request.ContentLength = 0; request.ContentType = ""; } // Notify WebRequestManager.Instance.NotifyWebRequestExecuting(request); // get the response return WebRequestStreamHelper.GetResponse(request); } catch (WebException e) { // stop the watch StopStopWatch(null, null, e); // rethrow the exception throw; } } /// /// This method returns an response stream /// /// /// public virtual Stream GetResponseStream(WebResponse response) { Stream responseStream = null; try { // get the network stream var nwStream = WebRequestStreamHelper.GetResponseStream(response); // get the response stream responseStream = new WebResponseStream(nwStream, response, this); } catch (WebException) { return null; } finally { StopStopWatch(response, responseStream, null); } // return the stream return responseStream; } /// /// This method executes alle dispose code for webresponse streams /// /// /// public virtual void DisposeWebResponseStreams(WebResponse response, WebResponseStream stream) { } /// /// Please override this method to ensure that the system can evaluate the /// protocol specific error code /// /// /// protected abstract int GetWebResponseStatus(WebResponse response); #endregion #region Comfort Functions /// /// Performs a five webrequest and returns the result as an in memory stream /// /// /// /// public MemoryStream PerformWebRequest(WebRequest request, object context) { int code; WebException e; return PerformWebRequest(request, context, out code, out e); } /// /// Performs a five webrequest and returns the result as an in memory stream /// /// /// /// /// /// public MemoryStream PerformWebRequest(WebRequest request, object context, out int protocolSpecificStatusCode, out WebException errorInfo) { return PerformWebRequest(request, context, out protocolSpecificStatusCode, out errorInfo, null); } /// /// Performs a five webrequest and returns the result as an in memory stream /// /// /// /// /// /// /// public MemoryStream PerformWebRequest(WebRequest request, object context, out int protocolSpecificStatusCode, out WebException errorInfo, Func httpCodeCondition) { WebHeaderCollection headers; return PerformWebRequest(request, context, null, out protocolSpecificStatusCode, out errorInfo, out headers, httpCodeCondition); } /// /// Performs a five webrequest and returns the result as an in memory stream /// /// /// /// /// /// /// /// public MemoryStream PerformWebRequest(WebRequest request, object context, Stream content, out int protocolSpecificStatusCode, out WebException errorInfo, out WebHeaderCollection headers) { return PerformWebRequest(request, context, content, out protocolSpecificStatusCode, out errorInfo, out headers, null); } /// /// Performs a five webrequest and returns the result as an in memory stream /// /// /// /// /// /// /// /// /// public MemoryStream PerformWebRequest(WebRequest request, object context, Stream content, out int protocolSpecificStatusCode, out WebException errorInfo, out WebHeaderCollection headers, Func httpCodeCondition) { // no error errorInfo = null; headers = null; // start try { // add content if needed if (content != null) { using (Stream requestStream = GetRequestStream(request, content.Length)) { StreamHelper.CopyStreamData(this, content, requestStream, null, null); } } // create the memstream var memStream = new MemoryStream(); // get the response using (var response = GetWebResponse(request)) { // set the error code protocolSpecificStatusCode = GetWebResponseStatus(response); // handle move if (protocolSpecificStatusCode == (int)HttpStatusCode.Moved) { // set the headers headers = response.Headers; } else if (httpCodeCondition != null && httpCodeCondition(protocolSpecificStatusCode)) { //Just return. Do nothing } else { // read the data using (var responseStream = GetResponseStream(response)) { // copy the data into memory StreamHelper.CopyStreamData(this, responseStream, memStream, null, null); // reset the memory stream memStream.Position = 0; // close the source stream responseStream.Close(); } } // close the response response.Close(); } // return the byte stream return memStream; } catch (WebException e) { if (e.Response == null) protocolSpecificStatusCode = (int)HttpStatusCode.BadRequest; else protocolSpecificStatusCode = GetWebResponseStatus(e.Response); errorInfo = e; return null; } } /// /// Forms a webrequest and performs them. The result will generated as memory stream /// /// /// /// /// /// public MemoryStream PerformSimpleWebCall(string url, string method, ICredentials credentials, object context) { int code; WebException e; return PerformSimpleWebCall(url, method, credentials, context, out code, out e); } /// /// Forms a webrequest and performs them. The result will generated as memory stream /// /// /// /// /// /// /// /// public MemoryStream PerformSimpleWebCall(string url, string method, ICredentials credentials, object context, out int code, out WebException errorInfo) { return PerformSimpleWebCall(url, method, credentials, null, context, out code, out errorInfo); } /// /// Forms a webrequest and performs them. The result will generated as memory stream /// /// /// /// /// /// /// /// /// public MemoryStream PerformSimpleWebCall(string url, string method, ICredentials credentials, Stream content, object context, out int code, out WebException errorInfo) { // create a web request var request = CreateWebRequest(url, method, credentials, false, context); if (request == null) throw new Exception("Could not generate WebRequest for " + method + ":" + url); WebHeaderCollection headers; var s = PerformWebRequest(request, context, content, out code, out errorInfo, out headers); if (code == (int)HttpStatusCode.Moved && request is HttpWebRequest) { // get new location var newLoc = headers["Location"]; // do it again return PerformSimpleWebCall(newLoc, method, credentials, content, context, out code, out errorInfo); } NetworkCredential networkCredential; if (code == (int)HttpStatusCode.Unauthorized && request is HttpWebRequest && (networkCredential = credentials as NetworkCredential) != null) { var search = new Regex(@"^\w+", RegexOptions.Singleline | RegexOptions.IgnoreCase); // get authentication method var authMethod = search.Match(errorInfo.Response.Headers["WWW-Authenticate"]).Value; var newCredentials = new CredentialCache { { new Uri((new Uri(url)).GetLeftPart(UriPartial.Authority)), authMethod, networkCredential } }; // do it again return PerformSimpleWebCall(url, method, newCredentials, content, context, out code, out errorInfo); } // go ahead return s; } #endregion #region Multi Part Form Data Support /// /// This method implements a standard multipart/form-data upload and can be overriden /// e.g. for WebDav upload /// /// /// /// public virtual WebRequest CreateWebRequestMultiPartUpload(string url, ICredentials credentials) { // 1. build a valid webrequest var request = CreateWebRequest(url, WebRequestMethodsEx.Http.Post, credentials, false, null); // 2. set the request paramter var mp = new WebRequestMultipartFormDataSupport(); mp.PrepareWebRequest(request); // 3. go ahead return request; } public virtual WebRequestStream GetRequestStreamMultiPartUpload(WebRequest request, string fileName, long fileSize) { // generate mp support var mp = new WebRequestMultipartFormDataSupport(); // set the right size fileSize = fileSize + mp.GetHeaderFooterSize(fileName); // set the streaming buffering if (fileSize > 0) { // set the maximum content length size request.ContentLength = fileSize; } // get the stream var stream = GetRequestStream(request, fileSize); // prepare the stream mp.PrepareRequestStream(stream, fileName); // go ahead return stream; } #endregion } }