From 72207382561e381908eab9864fef3c752ba0a7cc Mon Sep 17 00:00:00 2001 From: Scott Reed Date: Thu, 12 Jan 2017 19:17:19 -0800 Subject: [PATCH 1/2] Be able to simulate https (SSL/TLS) based requests --- HttpSimulator/HttpSimulator.cs | 921 +++++++++++++------------- HttpSimulator/SimulatedHttpRequest.cs | 158 ++--- 2 files changed, 544 insertions(+), 535 deletions(-) diff --git a/HttpSimulator/HttpSimulator.cs b/HttpSimulator/HttpSimulator.cs index 86fcdac..d865f7d 100644 --- a/HttpSimulator/HttpSimulator.cs +++ b/HttpSimulator/HttpSimulator.cs @@ -10,175 +10,176 @@ namespace Subtext.TestLibrary { - public enum HttpVerb - { - GET, - HEAD, - POST, - PUT, - DELETE, - } + public enum HttpVerb + { + GET, + HEAD, + POST, + PUT, + DELETE, + } /// - /// Useful class for simulating the HttpContext. This does not actually - /// make an HttpRequest, it merely simulates the state that your code - /// would be in "as if" handling a request. Thus the HttpContext.Current - /// property is populated. - /// - public class HttpSimulator : IDisposable - { - private const string defaultPhysicalAppPath = @"c:\InetPub\wwwRoot\"; - private StringBuilder builder; - private Uri _referer; - private NameValueCollection _formVars = new NameValueCollection(); - private NameValueCollection _headers = new NameValueCollection(); - - public HttpSimulator() : this("/", defaultPhysicalAppPath) - { - } - - public HttpSimulator(string applicationPath) : this(applicationPath, defaultPhysicalAppPath) - { - - } - - public HttpSimulator(string applicationPath, string physicalApplicationPath) - { - this.ApplicationPath = applicationPath; - this.PhysicalApplicationPath = physicalApplicationPath; - } - - /// - /// Sets up the HttpContext objects to simulate a GET request. - /// - /// - /// Simulates a request to http://localhost/ - /// - public HttpSimulator SimulateRequest() - { - return SimulateRequest(new Uri("http://localhost/")); - } - - /// - /// Sets up the HttpContext objects to simulate a GET request. - /// - /// - public HttpSimulator SimulateRequest(Uri url) - { - return SimulateRequest(url, HttpVerb.GET); - } - - /// - /// Sets up the HttpContext objects to simulate a request. - /// - /// - /// - public HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb) - { - return SimulateRequest(url, httpVerb, null, null); - } - - /// - /// Sets up the HttpContext objects to simulate a POST request. - /// - /// - /// - public HttpSimulator SimulateRequest(Uri url, NameValueCollection formVariables) - { - return SimulateRequest(url, HttpVerb.POST, formVariables, null); - } - - /// - /// Sets up the HttpContext objects to simulate a POST request. - /// - /// - /// - /// - public HttpSimulator SimulateRequest(Uri url, NameValueCollection formVariables, NameValueCollection headers) - { - return SimulateRequest(url, HttpVerb.POST, formVariables, headers); - } - - /// - /// Sets up the HttpContext objects to simulate a request. - /// - /// - /// - /// - public HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, NameValueCollection headers) - { - return SimulateRequest(url, httpVerb, null, headers); - } - - /// - /// Sets up the HttpContext objects to simulate a request. - /// - /// - /// - /// - /// - protected virtual HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, NameValueCollection formVariables, NameValueCollection headers) - { - HttpContext.Current = null; - - ParseRequestUrl(url); - - if (this.responseWriter == null) - { - this.builder = new StringBuilder(); - this.responseWriter = new StringWriter(builder); - } - - SetHttpRuntimeInternals(); - - string query = ExtractQueryStringPart(url); - - if (formVariables != null) - _formVars.Add(formVariables); - - if (_formVars.Count > 0) - httpVerb = HttpVerb.POST; //Need to enforce this. - - if (headers != null) - _headers.Add(headers); - - this.workerRequest = new SimulatedHttpRequest(ApplicationPath, PhysicalApplicationPath, PhysicalPath, Page, query, this.responseWriter, host, port, httpVerb.ToString()); - - this.workerRequest.Form.Add(_formVars); - this.workerRequest.Headers.Add(_headers); - - if (_referer != null) - this.workerRequest.SetReferer(_referer); - - InitializeSession(); + /// Useful class for simulating the HttpContext. This does not actually + /// make an HttpRequest, it merely simulates the state that your code + /// would be in "as if" handling a request. Thus the HttpContext.Current + /// property is populated. + /// + public class HttpSimulator : IDisposable + { + private const string defaultPhysicalAppPath = @"c:\InetPub\wwwRoot\"; + private StringBuilder builder; + private Uri _referer; + private NameValueCollection _formVars = new NameValueCollection(); + private NameValueCollection _headers = new NameValueCollection(); + + public HttpSimulator() : this("/", defaultPhysicalAppPath) + { + } + + public HttpSimulator(string applicationPath) : this(applicationPath, defaultPhysicalAppPath) + { + + } + + public HttpSimulator(string applicationPath, string physicalApplicationPath) + { + this.ApplicationPath = applicationPath; + this.PhysicalApplicationPath = physicalApplicationPath; + } + + /// + /// Sets up the HttpContext objects to simulate a GET request. + /// + /// + /// Simulates a request to http://localhost/ + /// + public HttpSimulator SimulateRequest() + { + return SimulateRequest(new Uri("http://localhost/")); + } + + /// + /// Sets up the HttpContext objects to simulate a GET request. + /// + /// + public HttpSimulator SimulateRequest(Uri url) + { + return SimulateRequest(url, HttpVerb.GET); + } + + /// + /// Sets up the HttpContext objects to simulate a request. + /// + /// + /// + public HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb) + { + return SimulateRequest(url, httpVerb, null, null); + } + + /// + /// Sets up the HttpContext objects to simulate a POST request. + /// + /// + /// + public HttpSimulator SimulateRequest(Uri url, NameValueCollection formVariables) + { + return SimulateRequest(url, HttpVerb.POST, formVariables, null); + } + + /// + /// Sets up the HttpContext objects to simulate a POST request. + /// + /// + /// + /// + public HttpSimulator SimulateRequest(Uri url, NameValueCollection formVariables, NameValueCollection headers) + { + return SimulateRequest(url, HttpVerb.POST, formVariables, headers); + } + + /// + /// Sets up the HttpContext objects to simulate a request. + /// + /// + /// + /// + public HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, NameValueCollection headers) + { + return SimulateRequest(url, httpVerb, null, headers); + } + + /// + /// Sets up the HttpContext objects to simulate a request. + /// + /// + /// + /// + /// + protected virtual HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, NameValueCollection formVariables, NameValueCollection headers) + { + HttpContext.Current = null; + + ParseRequestUrl(url); + + if (this.responseWriter == null) + { + this.builder = new StringBuilder(); + this.responseWriter = new StringWriter(builder); + } + + SetHttpRuntimeInternals(); + + string query = ExtractQueryStringPart(url); + + if (formVariables != null) + _formVars.Add(formVariables); + + if (_formVars.Count > 0) + httpVerb = HttpVerb.POST; //Need to enforce this. + + if (headers != null) + _headers.Add(headers); + + var isSecure = url.AbsoluteUri.StartsWith("https:"); + + this.workerRequest = new SimulatedHttpRequest(ApplicationPath, PhysicalApplicationPath, PhysicalPath, Page, query, this.responseWriter, host, port, httpVerb.ToString(), isSecure); + this.workerRequest.Form.Add(_formVars); + this.workerRequest.Headers.Add(_headers); + + if (_referer != null) + this.workerRequest.SetReferer(_referer); + + InitializeSession(); InitializeApplication(); - - #region Console Debug INfo - - Console.WriteLine("host: " + host); - Console.WriteLine("virtualDir: " + applicationPath); - Console.WriteLine("page: " + localPath); - Console.WriteLine("pathPartAfterApplicationPart: " + _page); - Console.WriteLine("appPhysicalDir: " + physicalApplicationPath); - Console.WriteLine("Request.Url.LocalPath: " + HttpContext.Current.Request.Url.LocalPath); - Console.WriteLine("Request.Url.Host: " + HttpContext.Current.Request.Url.Host); - Console.WriteLine("Request.FilePath: " + HttpContext.Current.Request.FilePath); - Console.WriteLine("Request.Path: " + HttpContext.Current.Request.Path); - Console.WriteLine("Request.RawUrl: " + HttpContext.Current.Request.RawUrl); - Console.WriteLine("Request.Url: " + HttpContext.Current.Request.Url); - Console.WriteLine("Request.Url.Port: " + HttpContext.Current.Request.Url.Port); - Console.WriteLine("Request.ApplicationPath: " + HttpContext.Current.Request.ApplicationPath); - Console.WriteLine("Request.PhysicalPath: " + HttpContext.Current.Request.PhysicalPath); - Console.WriteLine("HttpRuntime.AppDomainAppPath: " + HttpRuntime.AppDomainAppPath); - Console.WriteLine("HttpRuntime.AppDomainAppVirtualPath: " + HttpRuntime.AppDomainAppVirtualPath); - Console.WriteLine("HostingEnvironment.ApplicationPhysicalPath: " + HostingEnvironment.ApplicationPhysicalPath); - Console.WriteLine("HostingEnvironment.ApplicationVirtualPath: " + HostingEnvironment.ApplicationVirtualPath); - - #endregion - - return this; - } + + #region Console Debug INfo + + Console.WriteLine("host: " + host); + Console.WriteLine("virtualDir: " + applicationPath); + Console.WriteLine("page: " + localPath); + Console.WriteLine("pathPartAfterApplicationPart: " + _page); + Console.WriteLine("appPhysicalDir: " + physicalApplicationPath); + Console.WriteLine("Request.Url.LocalPath: " + HttpContext.Current.Request.Url.LocalPath); + Console.WriteLine("Request.Url.Host: " + HttpContext.Current.Request.Url.Host); + Console.WriteLine("Request.FilePath: " + HttpContext.Current.Request.FilePath); + Console.WriteLine("Request.Path: " + HttpContext.Current.Request.Path); + Console.WriteLine("Request.RawUrl: " + HttpContext.Current.Request.RawUrl); + Console.WriteLine("Request.Url: " + HttpContext.Current.Request.Url); + Console.WriteLine("Request.Url.Port: " + HttpContext.Current.Request.Url.Port); + Console.WriteLine("Request.ApplicationPath: " + HttpContext.Current.Request.ApplicationPath); + Console.WriteLine("Request.PhysicalPath: " + HttpContext.Current.Request.PhysicalPath); + Console.WriteLine("HttpRuntime.AppDomainAppPath: " + HttpRuntime.AppDomainAppPath); + Console.WriteLine("HttpRuntime.AppDomainAppVirtualPath: " + HttpRuntime.AppDomainAppVirtualPath); + Console.WriteLine("HostingEnvironment.ApplicationPhysicalPath: " + HostingEnvironment.ApplicationPhysicalPath); + Console.WriteLine("HostingEnvironment.ApplicationVirtualPath: " + HostingEnvironment.ApplicationVirtualPath); + + #endregion + + return this; + } private static void InitializeApplication() { @@ -212,7 +213,7 @@ public class FakeHttpSessionState : NameObjectCollectionBase, IHttpSessionState /// public void Abandon() { - BaseClear(); + BaseClear(); } /// @@ -438,7 +439,7 @@ public object SyncRoot get { return syncRoot; } } - + /// ///Gets a value indicating whether access to the collection of session-state values is synchronized (thread safe). @@ -469,63 +470,63 @@ bool IHttpSessionState.IsReadOnly } } - /// - /// Sets the referer for the request. Uses a fluent interface. - /// - /// - /// - public HttpSimulator SetReferer(Uri referer) - { - if(this.workerRequest != null) - this.workerRequest.SetReferer(referer); - this._referer = referer; - return this; - } - - /// - /// Sets a form variable. - /// - /// - /// - /// - public HttpSimulator SetFormVariable(string name, string value) - { - //TODO: Change this ordering requirement. - if (this.workerRequest != null) - throw new InvalidOperationException("Cannot set form variables after calling Simulate()."); - - _formVars.Add(name, value); - - return this; - } - - /// - /// Sets a header value. - /// - /// - /// - /// - public HttpSimulator SetHeader(string name, string value) - { - //TODO: Change this ordering requirement. - if (this.workerRequest != null) - throw new InvalidOperationException("Cannot set headers after calling Simulate()."); - - _headers.Add(name, value); - - return this; - } - - private void ParseRequestUrl(Uri url) - { - if (url == null) - return; - this.host = url.Host; - this.port = url.Port; - this.localPath = url.LocalPath; - this._page = StripPrecedingSlashes(RightAfter(url.LocalPath, ApplicationPath)); - this.physicalPath = Path.Combine(this.physicalApplicationPath, this._page.Replace("/", @"\")); - } + /// + /// Sets the referer for the request. Uses a fluent interface. + /// + /// + /// + public HttpSimulator SetReferer(Uri referer) + { + if (this.workerRequest != null) + this.workerRequest.SetReferer(referer); + this._referer = referer; + return this; + } + + /// + /// Sets a form variable. + /// + /// + /// + /// + public HttpSimulator SetFormVariable(string name, string value) + { + //TODO: Change this ordering requirement. + if (this.workerRequest != null) + throw new InvalidOperationException("Cannot set form variables after calling Simulate()."); + + _formVars.Add(name, value); + + return this; + } + + /// + /// Sets a header value. + /// + /// + /// + /// + public HttpSimulator SetHeader(string name, string value) + { + //TODO: Change this ordering requirement. + if (this.workerRequest != null) + throw new InvalidOperationException("Cannot set headers after calling Simulate()."); + + _headers.Add(name, value); + + return this; + } + + private void ParseRequestUrl(Uri url) + { + if (url == null) + return; + this.host = url.Host; + this.port = url.Port; + this.localPath = url.LocalPath; + this._page = StripPrecedingSlashes(RightAfter(url.LocalPath, ApplicationPath)); + this.physicalPath = Path.Combine(this.physicalApplicationPath, this._page.Replace("/", @"\")); + } static string RightAfter(string original, string search) { @@ -540,240 +541,240 @@ static string RightAfter(string original, string search) return original.Substring(original.IndexOf(search) + search.Length); } - public string Host - { - get { return this.host; } - } - - private string host; - - public string LocalPath - { - get { return this.localPath; } - } - - private string localPath; - - public int Port - { - get { return this.port; } - } - - private int port; - - /// - /// Portion of the URL after the application. - /// - public string Page - { - get { return this._page; } - } - - private string _page; - - /// - /// The same thing as the IIS Virtual directory. It's - /// what gets returned by Request.ApplicationPath. - /// - public string ApplicationPath - { - get { return this.applicationPath; } - set - { - this.applicationPath = value ?? "/"; - this.applicationPath = NormalizeSlashes(this.applicationPath); - } - } - private string applicationPath = "/"; - - /// - /// Physical path to the application (used for simulation purposes). - /// - public string PhysicalApplicationPath - { - get { return this.physicalApplicationPath; } - set - { - this.physicalApplicationPath = value ?? defaultPhysicalAppPath; - //strip trailing backslashes. - this.physicalApplicationPath = StripTrailingBackSlashes(this.physicalApplicationPath) + @"\"; - } - } - - private string physicalApplicationPath = defaultPhysicalAppPath; - - /// - /// Physical path to the requested file (used for simulation purposes). - /// - public string PhysicalPath - { - get { return this.physicalPath; } - } - - private string physicalPath = defaultPhysicalAppPath; - - public TextWriter ResponseWriter - { - get { return this.responseWriter; } - set { this.responseWriter = value; } - } - - /// - /// Returns the text from the response to the simulated request. - /// - public string ResponseText - { - get - { - return (builder ?? new StringBuilder()).ToString(); - } - } - - private TextWriter responseWriter; - - public SimulatedHttpRequest WorkerRequest - { - get { return this.workerRequest; } - } - - private SimulatedHttpRequest workerRequest; - - private static string ExtractQueryStringPart(Uri url) - { - string query = url.Query ?? string.Empty; - if(query.StartsWith("?")) - return query.Substring(1); - return query; - } - - void SetHttpRuntimeInternals() - { - //We cheat by using reflection. - - // get singleton property value - HttpRuntime runtime = ReflectionHelper.GetStaticFieldValue("_theRuntime", typeof (HttpRuntime)); - - // set app path property value - ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppPath", runtime, PhysicalApplicationPath); - // set app virtual path property value - string vpathTypeName = "System.Web.VirtualPath, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; - object virtualPath = ReflectionHelper.Instantiate(vpathTypeName, new Type[] { typeof(string) }, new object[] { ApplicationPath }); - ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppVPath", runtime, virtualPath); - - // set codegen dir property value - ReflectionHelper.SetPrivateInstanceFieldValue("_codegenDir", runtime, PhysicalApplicationPath); - - HostingEnvironment environment = GetHostingEnvironment(); - ReflectionHelper.SetPrivateInstanceFieldValue("_appPhysicalPath", environment, PhysicalApplicationPath); - ReflectionHelper.SetPrivateInstanceFieldValue("_appVirtualPath", environment, virtualPath); - ReflectionHelper.SetPrivateInstanceFieldValue("_configMapPath", environment, new ConfigMapPath(this)); - } - - protected static HostingEnvironment GetHostingEnvironment() - { - HostingEnvironment environment; - try - { - environment = new HostingEnvironment(); - } - catch (InvalidOperationException) - { - //Shoot, we need to grab it via reflection. - environment = ReflectionHelper.GetStaticFieldValue("_theHostingEnvironment", typeof(HostingEnvironment)); - } - return environment; - } - - #region --- Text Manipulation Methods for slashes --- - protected static string NormalizeSlashes(string s) - { - if (String.IsNullOrEmpty(s) || s == "/") - return "/"; - - s = s.Replace(@"\", "/"); - - //Reduce multiple slashes in row to single. - string normalized = Regex.Replace(s, "(/)/+", "$1"); - //Strip left. - normalized = StripPrecedingSlashes(normalized); - //Strip right. - normalized = StripTrailingSlashes(normalized); - return "/" + normalized; - } - - protected static string StripPrecedingSlashes(string s) - { - return Regex.Replace(s, "^/*(.*)", "$1"); - } - - protected static string StripTrailingSlashes(string s) - { - return Regex.Replace(s, "(.*)/*$", "$1", RegexOptions.RightToLeft); - } - - protected static string StripTrailingBackSlashes(string s) - { - if (String.IsNullOrEmpty(s)) - return string.Empty; - return Regex.Replace(s, @"(.*)\\*$", "$1", RegexOptions.RightToLeft); - } - #endregion - - internal class ConfigMapPath : IConfigMapPath - { - private HttpSimulator _requestSimulation; - public ConfigMapPath(HttpSimulator simulation) - { - _requestSimulation = simulation; - } - - public string GetMachineConfigFilename() - { - throw new NotImplementedException(); - } - - public string GetRootWebConfigFilename() - { - throw new NotImplementedException(); - } - - public void GetPathConfigFilename(string siteID, string path, out string directory, out string baseName) - { - throw new NotImplementedException(); - } - - public void GetDefaultSiteNameAndID(out string siteName, out string siteID) - { - throw new NotImplementedException(); - } - - public void ResolveSiteArgument(string siteArgument, out string siteName, out string siteID) - { - throw new NotImplementedException(); - } - - public string MapPath(string siteID, string path) - { - string page = StripPrecedingSlashes(RightAfter(path, _requestSimulation.ApplicationPath)); - return Path.Combine(_requestSimulation.PhysicalApplicationPath, page.Replace("/", @"\")); - } - - public string GetAppPathForPath(string siteID, string path) - { - return _requestSimulation.ApplicationPath; - } - } - - /// - ///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - ///2 - public void Dispose() - { - if(HttpContext.Current != null) - { - HttpContext.Current = null; - } - } - } + public string Host + { + get { return this.host; } + } + + private string host; + + public string LocalPath + { + get { return this.localPath; } + } + + private string localPath; + + public int Port + { + get { return this.port; } + } + + private int port; + + /// + /// Portion of the URL after the application. + /// + public string Page + { + get { return this._page; } + } + + private string _page; + + /// + /// The same thing as the IIS Virtual directory. It's + /// what gets returned by Request.ApplicationPath. + /// + public string ApplicationPath + { + get { return this.applicationPath; } + set + { + this.applicationPath = value ?? "/"; + this.applicationPath = NormalizeSlashes(this.applicationPath); + } + } + private string applicationPath = "/"; + + /// + /// Physical path to the application (used for simulation purposes). + /// + public string PhysicalApplicationPath + { + get { return this.physicalApplicationPath; } + set + { + this.physicalApplicationPath = value ?? defaultPhysicalAppPath; + //strip trailing backslashes. + this.physicalApplicationPath = StripTrailingBackSlashes(this.physicalApplicationPath) + @"\"; + } + } + + private string physicalApplicationPath = defaultPhysicalAppPath; + + /// + /// Physical path to the requested file (used for simulation purposes). + /// + public string PhysicalPath + { + get { return this.physicalPath; } + } + + private string physicalPath = defaultPhysicalAppPath; + + public TextWriter ResponseWriter + { + get { return this.responseWriter; } + set { this.responseWriter = value; } + } + + /// + /// Returns the text from the response to the simulated request. + /// + public string ResponseText + { + get + { + return (builder ?? new StringBuilder()).ToString(); + } + } + + private TextWriter responseWriter; + + public SimulatedHttpRequest WorkerRequest + { + get { return this.workerRequest; } + } + + private SimulatedHttpRequest workerRequest; + + private static string ExtractQueryStringPart(Uri url) + { + string query = url.Query ?? string.Empty; + if (query.StartsWith("?")) + return query.Substring(1); + return query; + } + + void SetHttpRuntimeInternals() + { + //We cheat by using reflection. + + // get singleton property value + HttpRuntime runtime = ReflectionHelper.GetStaticFieldValue("_theRuntime", typeof(HttpRuntime)); + + // set app path property value + ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppPath", runtime, PhysicalApplicationPath); + // set app virtual path property value + string vpathTypeName = "System.Web.VirtualPath, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; + object virtualPath = ReflectionHelper.Instantiate(vpathTypeName, new Type[] { typeof(string) }, new object[] { ApplicationPath }); + ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppVPath", runtime, virtualPath); + + // set codegen dir property value + ReflectionHelper.SetPrivateInstanceFieldValue("_codegenDir", runtime, PhysicalApplicationPath); + + HostingEnvironment environment = GetHostingEnvironment(); + ReflectionHelper.SetPrivateInstanceFieldValue("_appPhysicalPath", environment, PhysicalApplicationPath); + ReflectionHelper.SetPrivateInstanceFieldValue("_appVirtualPath", environment, virtualPath); + ReflectionHelper.SetPrivateInstanceFieldValue("_configMapPath", environment, new ConfigMapPath(this)); + } + + protected static HostingEnvironment GetHostingEnvironment() + { + HostingEnvironment environment; + try + { + environment = new HostingEnvironment(); + } + catch (InvalidOperationException) + { + //Shoot, we need to grab it via reflection. + environment = ReflectionHelper.GetStaticFieldValue("_theHostingEnvironment", typeof(HostingEnvironment)); + } + return environment; + } + + #region --- Text Manipulation Methods for slashes --- + protected static string NormalizeSlashes(string s) + { + if (String.IsNullOrEmpty(s) || s == "/") + return "/"; + + s = s.Replace(@"\", "/"); + + //Reduce multiple slashes in row to single. + string normalized = Regex.Replace(s, "(/)/+", "$1"); + //Strip left. + normalized = StripPrecedingSlashes(normalized); + //Strip right. + normalized = StripTrailingSlashes(normalized); + return "/" + normalized; + } + + protected static string StripPrecedingSlashes(string s) + { + return Regex.Replace(s, "^/*(.*)", "$1"); + } + + protected static string StripTrailingSlashes(string s) + { + return Regex.Replace(s, "(.*)/*$", "$1", RegexOptions.RightToLeft); + } + + protected static string StripTrailingBackSlashes(string s) + { + if (String.IsNullOrEmpty(s)) + return string.Empty; + return Regex.Replace(s, @"(.*)\\*$", "$1", RegexOptions.RightToLeft); + } + #endregion + + internal class ConfigMapPath : IConfigMapPath + { + private HttpSimulator _requestSimulation; + public ConfigMapPath(HttpSimulator simulation) + { + _requestSimulation = simulation; + } + + public string GetMachineConfigFilename() + { + throw new NotImplementedException(); + } + + public string GetRootWebConfigFilename() + { + throw new NotImplementedException(); + } + + public void GetPathConfigFilename(string siteID, string path, out string directory, out string baseName) + { + throw new NotImplementedException(); + } + + public void GetDefaultSiteNameAndID(out string siteName, out string siteID) + { + throw new NotImplementedException(); + } + + public void ResolveSiteArgument(string siteArgument, out string siteName, out string siteID) + { + throw new NotImplementedException(); + } + + public string MapPath(string siteID, string path) + { + string page = StripPrecedingSlashes(RightAfter(path, _requestSimulation.ApplicationPath)); + return Path.Combine(_requestSimulation.PhysicalApplicationPath, page.Replace("/", @"\")); + } + + public string GetAppPathForPath(string siteID, string path) + { + return _requestSimulation.ApplicationPath; + } + } + + /// + ///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + ///2 + public void Dispose() + { + if (HttpContext.Current != null) + { + HttpContext.Current = null; + } + } + } } diff --git a/HttpSimulator/SimulatedHttpRequest.cs b/HttpSimulator/SimulatedHttpRequest.cs index b013db4..e0a4267 100644 --- a/HttpSimulator/SimulatedHttpRequest.cs +++ b/HttpSimulator/SimulatedHttpRequest.cs @@ -26,47 +26,55 @@ namespace Subtext.TestLibrary /// public class SimulatedHttpRequest : SimpleWorkerRequest { - Uri _referer; + Uri _referer; string _host; string _verb; - int _port; - string _physicalFilePath; - - /// - /// Creates a new instance. - /// - /// App virtual dir. - /// Physical Path to the app. - /// Physical Path to the file. - /// The Part of the URL after the application. - /// Query. - /// Output. - /// Host. - /// Port to request. - /// The HTTP Verb to use. - public SimulatedHttpRequest(string applicationPath, string physicalAppPath, string physicalFilePath, string page, string query, TextWriter output, string host, int port, string verb) : base(applicationPath, physicalAppPath, page, query, output) - { - if (host == null) - throw new ArgumentNullException("host", "Host cannot be null."); - - if(host.Length == 0) - throw new ArgumentException("Host cannot be empty.", "host"); - - if (applicationPath == null) - throw new ArgumentNullException("applicationPath", "Can't create a request with a null application path. Try empty string."); - - _host = host; - _verb = verb; - _port = port; - _physicalFilePath = physicalFilePath; - } - - internal void SetReferer(Uri referer) - { - _referer = referer; - } - - /// + private readonly bool _isSecure; + int _port; + string _physicalFilePath; + + /// + /// Creates a new instance. + /// + /// App virtual dir. + /// Physical Path to the app. + /// Physical Path to the file. + /// The Part of the URL after the application. + /// Query. + /// Output. + /// Host. + /// Port to request. + /// The HTTP Verb to use. + /// + public SimulatedHttpRequest(string applicationPath, string physicalAppPath, string physicalFilePath, string page, string query, TextWriter output, string host, int port, string verb, bool isSecure = false) : base(applicationPath, physicalAppPath, page, query, output) + { + if (host == null) + throw new ArgumentNullException("host", "Host cannot be null."); + + if (host.Length == 0) + throw new ArgumentException("Host cannot be empty.", "host"); + + if (applicationPath == null) + throw new ArgumentNullException("applicationPath", "Can't create a request with a null application path. Try empty string."); + + _host = host; + _verb = verb; + _isSecure = isSecure; + _port = port; + _physicalFilePath = physicalFilePath; + } + + public override bool IsSecure() + { + return _isSecure; + } + + internal void SetReferer(Uri referer) + { + _referer = referer; + } + + /// /// Returns the specified member of the request header. /// /// @@ -77,7 +85,7 @@ public override string GetHttpVerbName() { return _verb; } - + /// /// Gets the name of the server. /// @@ -86,11 +94,11 @@ public override string GetServerName() { return _host; } - - public override int GetLocalPort() - { - return this._port; - } + + public override int GetLocalPort() + { + return this._port; + } /// /// Gets the headers. @@ -105,7 +113,7 @@ public NameValueCollection Headers } private NameValueCollection headers = new NameValueCollection(); - + /// /// Gets the format exception. /// @@ -125,12 +133,12 @@ public NameValueCollection Form /// An array of header name-value pairs. public override string[][] GetUnknownRequestHeaders() { - if(this.headers == null || this.headers.Count == 0) + if (this.headers == null || this.headers.Count == 0) { return null; } string[][] headersArray = new string[this.headers.Count][]; - for(int i = 0; i < this.headers.Count; i++) + for (int i = 0; i < this.headers.Count; i++) { headersArray[i] = new string[2]; headersArray[i][0] = this.headers.Keys[i]; @@ -139,16 +147,16 @@ public override string[][] GetUnknownRequestHeaders() return headersArray; } - public override string GetKnownRequestHeader(int index) - { - if (index == 0x24) + public override string GetKnownRequestHeader(int index) + { + if (index == 0x24) return _referer == null ? string.Empty : _referer.ToString(); - if (index == 12 && this._verb == "POST") - return "application/x-www-form-urlencoded"; - - return base.GetKnownRequestHeader(index); - } + if (index == 12 && this._verb == "POST") + return "application/x-www-form-urlencoded"; + + return base.GetKnownRequestHeader(index); + } /// /// Returns the virtual path to the currently executing @@ -163,23 +171,23 @@ public override string GetAppPath() return appPath; } - public override string GetAppPathTranslated() - { - string path = base.GetAppPathTranslated(); - return path; - } + public override string GetAppPathTranslated() + { + string path = base.GetAppPathTranslated(); + return path; + } - public override string GetUriPath() - { - string uriPath = base.GetUriPath(); - return uriPath; - } + public override string GetUriPath() + { + string uriPath = base.GetUriPath(); + return uriPath; + } + + public override string GetFilePathTranslated() + { + return _physicalFilePath; + } - public override string GetFilePathTranslated() - { - return _physicalFilePath; - } - /// /// Reads request data from the client (when not preloaded). /// @@ -187,15 +195,15 @@ public override string GetFilePathTranslated() public override byte[] GetPreloadedEntityBody() { string formText = string.Empty; - - foreach(string key in this.formVariables.Keys) + + foreach (string key in this.formVariables.Keys) { formText += string.Format("{0}={1}&", key, this.formVariables[key]); } - + return Encoding.UTF8.GetBytes(formText); } - + /// /// Returns a value indicating whether all request data /// is available and no further reads from the client are required. From a0f04eb650f48fc1539415878c24bd812a473b68 Mon Sep 17 00:00:00 2001 From: Scott Reed Date: Thu, 12 Jan 2017 19:22:18 -0800 Subject: [PATCH 2/2] right, spaces, even though I support tabs (go Richard), your repo, your rules --- HttpSimulator/HttpSimulator.cs | 1534 ++++++++++++------------- HttpSimulator/SimulatedHttpRequest.cs | 392 +++---- 2 files changed, 963 insertions(+), 963 deletions(-) diff --git a/HttpSimulator/HttpSimulator.cs b/HttpSimulator/HttpSimulator.cs index d865f7d..0ed2df9 100644 --- a/HttpSimulator/HttpSimulator.cs +++ b/HttpSimulator/HttpSimulator.cs @@ -10,771 +10,771 @@ namespace Subtext.TestLibrary { - public enum HttpVerb - { - GET, - HEAD, - POST, - PUT, - DELETE, - } - - /// - /// Useful class for simulating the HttpContext. This does not actually - /// make an HttpRequest, it merely simulates the state that your code - /// would be in "as if" handling a request. Thus the HttpContext.Current - /// property is populated. - /// - public class HttpSimulator : IDisposable - { - private const string defaultPhysicalAppPath = @"c:\InetPub\wwwRoot\"; - private StringBuilder builder; - private Uri _referer; - private NameValueCollection _formVars = new NameValueCollection(); - private NameValueCollection _headers = new NameValueCollection(); - - public HttpSimulator() : this("/", defaultPhysicalAppPath) - { - } - - public HttpSimulator(string applicationPath) : this(applicationPath, defaultPhysicalAppPath) - { - - } - - public HttpSimulator(string applicationPath, string physicalApplicationPath) - { - this.ApplicationPath = applicationPath; - this.PhysicalApplicationPath = physicalApplicationPath; - } - - /// - /// Sets up the HttpContext objects to simulate a GET request. - /// - /// - /// Simulates a request to http://localhost/ - /// - public HttpSimulator SimulateRequest() - { - return SimulateRequest(new Uri("http://localhost/")); - } - - /// - /// Sets up the HttpContext objects to simulate a GET request. - /// - /// - public HttpSimulator SimulateRequest(Uri url) - { - return SimulateRequest(url, HttpVerb.GET); - } - - /// - /// Sets up the HttpContext objects to simulate a request. - /// - /// - /// - public HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb) - { - return SimulateRequest(url, httpVerb, null, null); - } - - /// - /// Sets up the HttpContext objects to simulate a POST request. - /// - /// - /// - public HttpSimulator SimulateRequest(Uri url, NameValueCollection formVariables) - { - return SimulateRequest(url, HttpVerb.POST, formVariables, null); - } - - /// - /// Sets up the HttpContext objects to simulate a POST request. - /// - /// - /// - /// - public HttpSimulator SimulateRequest(Uri url, NameValueCollection formVariables, NameValueCollection headers) - { - return SimulateRequest(url, HttpVerb.POST, formVariables, headers); - } - - /// - /// Sets up the HttpContext objects to simulate a request. - /// - /// - /// - /// - public HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, NameValueCollection headers) - { - return SimulateRequest(url, httpVerb, null, headers); - } - - /// - /// Sets up the HttpContext objects to simulate a request. - /// - /// - /// - /// - /// - protected virtual HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, NameValueCollection formVariables, NameValueCollection headers) - { - HttpContext.Current = null; - - ParseRequestUrl(url); - - if (this.responseWriter == null) - { - this.builder = new StringBuilder(); - this.responseWriter = new StringWriter(builder); - } - - SetHttpRuntimeInternals(); - - string query = ExtractQueryStringPart(url); - - if (formVariables != null) - _formVars.Add(formVariables); - - if (_formVars.Count > 0) - httpVerb = HttpVerb.POST; //Need to enforce this. - - if (headers != null) - _headers.Add(headers); - - var isSecure = url.AbsoluteUri.StartsWith("https:"); - - this.workerRequest = new SimulatedHttpRequest(ApplicationPath, PhysicalApplicationPath, PhysicalPath, Page, query, this.responseWriter, host, port, httpVerb.ToString(), isSecure); - this.workerRequest.Form.Add(_formVars); - this.workerRequest.Headers.Add(_headers); - - if (_referer != null) - this.workerRequest.SetReferer(_referer); - - InitializeSession(); - - InitializeApplication(); - - #region Console Debug INfo - - Console.WriteLine("host: " + host); - Console.WriteLine("virtualDir: " + applicationPath); - Console.WriteLine("page: " + localPath); - Console.WriteLine("pathPartAfterApplicationPart: " + _page); - Console.WriteLine("appPhysicalDir: " + physicalApplicationPath); - Console.WriteLine("Request.Url.LocalPath: " + HttpContext.Current.Request.Url.LocalPath); - Console.WriteLine("Request.Url.Host: " + HttpContext.Current.Request.Url.Host); - Console.WriteLine("Request.FilePath: " + HttpContext.Current.Request.FilePath); - Console.WriteLine("Request.Path: " + HttpContext.Current.Request.Path); - Console.WriteLine("Request.RawUrl: " + HttpContext.Current.Request.RawUrl); - Console.WriteLine("Request.Url: " + HttpContext.Current.Request.Url); - Console.WriteLine("Request.Url.Port: " + HttpContext.Current.Request.Url.Port); - Console.WriteLine("Request.ApplicationPath: " + HttpContext.Current.Request.ApplicationPath); - Console.WriteLine("Request.PhysicalPath: " + HttpContext.Current.Request.PhysicalPath); - Console.WriteLine("HttpRuntime.AppDomainAppPath: " + HttpRuntime.AppDomainAppPath); - Console.WriteLine("HttpRuntime.AppDomainAppVirtualPath: " + HttpRuntime.AppDomainAppVirtualPath); - Console.WriteLine("HostingEnvironment.ApplicationPhysicalPath: " + HostingEnvironment.ApplicationPhysicalPath); - Console.WriteLine("HostingEnvironment.ApplicationVirtualPath: " + HostingEnvironment.ApplicationVirtualPath); - - #endregion - - return this; - } - - private static void InitializeApplication() - { - Type appFactoryType = Type.GetType("System.Web.HttpApplicationFactory, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); - object appFactory = ReflectionHelper.GetStaticFieldValue("_theApplicationFactory", appFactoryType); - ReflectionHelper.SetPrivateInstanceFieldValue("_state", appFactory, HttpContext.Current.Application); - } - - private void InitializeSession() - { - HttpContext.Current = new HttpContext(workerRequest); - HttpContext.Current.Items.Clear(); - HttpSessionState session = (HttpSessionState)ReflectionHelper.Instantiate(typeof(HttpSessionState), new Type[] { typeof(IHttpSessionState) }, new FakeHttpSessionState()); - - HttpContext.Current.Items.Add("AspSession", session); - } - - public class FakeHttpSessionState : NameObjectCollectionBase, IHttpSessionState - { - private string sessionID = Guid.NewGuid().ToString(); - private int timeout = 30; //minutes - private bool isNewSession = true; - private int lcid; - private int codePage; - private HttpStaticObjectsCollection staticObjects = new HttpStaticObjectsCollection(); - private object syncRoot = new Object(); - - /// - ///Ends the current session. - /// - /// - public void Abandon() - { - BaseClear(); - } - - /// - ///Adds a new item to the session-state collection. - /// - /// - ///The name of the item to add to the session-state collection. - ///The value of the item to add to the session-state collection. - public void Add(string name, object value) - { - BaseAdd(name, value); - } - - /// - ///Deletes an item from the session-state item collection. - /// - /// - ///The name of the item to delete from the session-state item collection. - public void Remove(string name) - { - BaseRemove(name); - } - - /// - ///Deletes an item at a specified index from the session-state item collection. - /// - /// - ///The index of the item to remove from the session-state collection. - public void RemoveAt(int index) - { - BaseRemoveAt(index); - } - - /// - ///Clears all values from the session-state item collection. - /// - /// - public void Clear() - { - BaseClear(); - } - - /// - ///Clears all values from the session-state item collection. - /// - /// - public void RemoveAll() - { - BaseClear(); - } - - /// - ///Copies the collection of session-state item values to a one-dimensional array, starting at the specified index in the array. - /// - /// - ///The that receives the session values. - ///The index in array where copying starts. - public void CopyTo(Array array, int index) - { - throw new NotImplementedException(); - } - - /// - ///Gets the unique session identifier for the session. - /// - /// - /// - ///The session ID. - /// - /// - public string SessionID - { - get { return sessionID; } - } - - /// - ///Gets and sets the time-out period (in minutes) allowed between requests before the session-state provider terminates the session. - /// - /// - /// - ///The time-out period, in minutes. - /// - /// - public int Timeout - { - get { return timeout; } - set { timeout = value; } - } - - /// - ///Gets a value indicating whether the session was created with the current request. - /// - /// - /// - ///true if the session was created with the current request; otherwise, false. - /// - /// - public bool IsNewSession - { - get { return isNewSession; } - } - - /// - ///Gets the current session-state mode. - /// - /// - /// - ///One of the values. - /// - /// - public SessionStateMode Mode - { - get { return SessionStateMode.InProc; } - } - - /// - ///Gets a value indicating whether the session ID is embedded in the URL or stored in an HTTP cookie. - /// - /// - /// - ///true if the session is embedded in the URL; otherwise, false. - /// - /// - public bool IsCookieless - { - get { return false; } - } - - /// - ///Gets a value that indicates whether the application is configured for cookieless sessions. - /// - /// - /// - ///One of the values that indicate whether the application is configured for cookieless sessions. The default is . - /// - /// - public HttpCookieMode CookieMode - { - get { return HttpCookieMode.UseCookies; } - } - - /// - ///Gets or sets the locale identifier (LCID) of the current session. - /// - /// - /// - ///A instance that specifies the culture of the current session. - /// - /// - public int LCID - { - get { return lcid; } - set { lcid = value; } - } - - /// - ///Gets or sets the code-page identifier for the current session. - /// - /// - /// - ///The code-page identifier for the current session. - /// - /// - public int CodePage - { - get { return codePage; } - set { codePage = value; } - } - - /// - ///Gets a collection of objects declared by <object Runat="Server" Scope="Session"/> tags within the ASP.NET application file Global.asax. - /// - /// - /// - ///An containing objects declared in the Global.asax file. - /// - /// - public HttpStaticObjectsCollection StaticObjects - { - get { return staticObjects; } - } - - /// - ///Gets or sets a session-state item value by name. - /// - /// - /// - ///The session-state item value specified in the name parameter. - /// - /// - ///The key name of the session-state item value. - public object this[string name] - { - get { return BaseGet(name); } - set { BaseSet(name, value); } - } - - /// - ///Gets or sets a session-state item value by numerical index. - /// - /// - /// - ///The session-state item value specified in the index parameter. - /// - /// - ///The numerical index of the session-state item value. - public object this[int index] - { - get { return BaseGet(index); } - set { BaseSet(index, value); } - } - - /// - ///Gets an object that can be used to synchronize access to the collection of session-state values. - /// - /// - /// - ///An object that can be used to synchronize access to the collection. - /// - /// - public object SyncRoot - { - get { return syncRoot; } - } - - - - /// - ///Gets a value indicating whether access to the collection of session-state values is synchronized (thread safe). - /// - /// - ///true if access to the collection is synchronized (thread safe); otherwise, false. - /// - /// - public bool IsSynchronized - { - get { return true; } - } - - /// - ///Gets a value indicating whether the session is read-only. - /// - /// - /// - ///true if the session is read-only; otherwise, false. - /// - /// - bool IHttpSessionState.IsReadOnly - { - get - { - return true; - } - } - } - - /// - /// Sets the referer for the request. Uses a fluent interface. - /// - /// - /// - public HttpSimulator SetReferer(Uri referer) - { - if (this.workerRequest != null) - this.workerRequest.SetReferer(referer); - this._referer = referer; - return this; - } - - /// - /// Sets a form variable. - /// - /// - /// - /// - public HttpSimulator SetFormVariable(string name, string value) - { - //TODO: Change this ordering requirement. - if (this.workerRequest != null) - throw new InvalidOperationException("Cannot set form variables after calling Simulate()."); - - _formVars.Add(name, value); - - return this; - } - - /// - /// Sets a header value. - /// - /// - /// - /// - public HttpSimulator SetHeader(string name, string value) - { - //TODO: Change this ordering requirement. - if (this.workerRequest != null) - throw new InvalidOperationException("Cannot set headers after calling Simulate()."); - - _headers.Add(name, value); - - return this; - } - - private void ParseRequestUrl(Uri url) - { - if (url == null) - return; - this.host = url.Host; - this.port = url.Port; - this.localPath = url.LocalPath; - this._page = StripPrecedingSlashes(RightAfter(url.LocalPath, ApplicationPath)); - this.physicalPath = Path.Combine(this.physicalApplicationPath, this._page.Replace("/", @"\")); - } - - static string RightAfter(string original, string search) - { - if (search.Length > original.Length || search.Length == 0) - return original; - - int searchIndex = original.IndexOf(search, 0, StringComparison.InvariantCultureIgnoreCase); - - if (searchIndex < 0) - return original; - - return original.Substring(original.IndexOf(search) + search.Length); - } - - public string Host - { - get { return this.host; } - } - - private string host; - - public string LocalPath - { - get { return this.localPath; } - } - - private string localPath; - - public int Port - { - get { return this.port; } - } - - private int port; - - /// - /// Portion of the URL after the application. - /// - public string Page - { - get { return this._page; } - } - - private string _page; - - /// - /// The same thing as the IIS Virtual directory. It's - /// what gets returned by Request.ApplicationPath. - /// - public string ApplicationPath - { - get { return this.applicationPath; } - set - { - this.applicationPath = value ?? "/"; - this.applicationPath = NormalizeSlashes(this.applicationPath); - } - } - private string applicationPath = "/"; - - /// - /// Physical path to the application (used for simulation purposes). - /// - public string PhysicalApplicationPath - { - get { return this.physicalApplicationPath; } - set - { - this.physicalApplicationPath = value ?? defaultPhysicalAppPath; - //strip trailing backslashes. - this.physicalApplicationPath = StripTrailingBackSlashes(this.physicalApplicationPath) + @"\"; - } - } - - private string physicalApplicationPath = defaultPhysicalAppPath; - - /// - /// Physical path to the requested file (used for simulation purposes). - /// - public string PhysicalPath - { - get { return this.physicalPath; } - } - - private string physicalPath = defaultPhysicalAppPath; - - public TextWriter ResponseWriter - { - get { return this.responseWriter; } - set { this.responseWriter = value; } - } - - /// - /// Returns the text from the response to the simulated request. - /// - public string ResponseText - { - get - { - return (builder ?? new StringBuilder()).ToString(); - } - } - - private TextWriter responseWriter; - - public SimulatedHttpRequest WorkerRequest - { - get { return this.workerRequest; } - } - - private SimulatedHttpRequest workerRequest; - - private static string ExtractQueryStringPart(Uri url) - { - string query = url.Query ?? string.Empty; - if (query.StartsWith("?")) - return query.Substring(1); - return query; - } - - void SetHttpRuntimeInternals() - { - //We cheat by using reflection. - - // get singleton property value - HttpRuntime runtime = ReflectionHelper.GetStaticFieldValue("_theRuntime", typeof(HttpRuntime)); - - // set app path property value - ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppPath", runtime, PhysicalApplicationPath); - // set app virtual path property value - string vpathTypeName = "System.Web.VirtualPath, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; - object virtualPath = ReflectionHelper.Instantiate(vpathTypeName, new Type[] { typeof(string) }, new object[] { ApplicationPath }); - ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppVPath", runtime, virtualPath); - - // set codegen dir property value - ReflectionHelper.SetPrivateInstanceFieldValue("_codegenDir", runtime, PhysicalApplicationPath); - - HostingEnvironment environment = GetHostingEnvironment(); - ReflectionHelper.SetPrivateInstanceFieldValue("_appPhysicalPath", environment, PhysicalApplicationPath); - ReflectionHelper.SetPrivateInstanceFieldValue("_appVirtualPath", environment, virtualPath); - ReflectionHelper.SetPrivateInstanceFieldValue("_configMapPath", environment, new ConfigMapPath(this)); - } - - protected static HostingEnvironment GetHostingEnvironment() - { - HostingEnvironment environment; - try - { - environment = new HostingEnvironment(); - } - catch (InvalidOperationException) - { - //Shoot, we need to grab it via reflection. - environment = ReflectionHelper.GetStaticFieldValue("_theHostingEnvironment", typeof(HostingEnvironment)); - } - return environment; - } - - #region --- Text Manipulation Methods for slashes --- - protected static string NormalizeSlashes(string s) - { - if (String.IsNullOrEmpty(s) || s == "/") - return "/"; - - s = s.Replace(@"\", "/"); - - //Reduce multiple slashes in row to single. - string normalized = Regex.Replace(s, "(/)/+", "$1"); - //Strip left. - normalized = StripPrecedingSlashes(normalized); - //Strip right. - normalized = StripTrailingSlashes(normalized); - return "/" + normalized; - } - - protected static string StripPrecedingSlashes(string s) - { - return Regex.Replace(s, "^/*(.*)", "$1"); - } - - protected static string StripTrailingSlashes(string s) - { - return Regex.Replace(s, "(.*)/*$", "$1", RegexOptions.RightToLeft); - } - - protected static string StripTrailingBackSlashes(string s) - { - if (String.IsNullOrEmpty(s)) - return string.Empty; - return Regex.Replace(s, @"(.*)\\*$", "$1", RegexOptions.RightToLeft); - } - #endregion - - internal class ConfigMapPath : IConfigMapPath - { - private HttpSimulator _requestSimulation; - public ConfigMapPath(HttpSimulator simulation) - { - _requestSimulation = simulation; - } - - public string GetMachineConfigFilename() - { - throw new NotImplementedException(); - } - - public string GetRootWebConfigFilename() - { - throw new NotImplementedException(); - } - - public void GetPathConfigFilename(string siteID, string path, out string directory, out string baseName) - { - throw new NotImplementedException(); - } - - public void GetDefaultSiteNameAndID(out string siteName, out string siteID) - { - throw new NotImplementedException(); - } - - public void ResolveSiteArgument(string siteArgument, out string siteName, out string siteID) - { - throw new NotImplementedException(); - } - - public string MapPath(string siteID, string path) - { - string page = StripPrecedingSlashes(RightAfter(path, _requestSimulation.ApplicationPath)); - return Path.Combine(_requestSimulation.PhysicalApplicationPath, page.Replace("/", @"\")); - } - - public string GetAppPathForPath(string siteID, string path) - { - return _requestSimulation.ApplicationPath; - } - } - - /// - ///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - ///2 - public void Dispose() - { - if (HttpContext.Current != null) - { - HttpContext.Current = null; - } - } - } + public enum HttpVerb + { + GET, + HEAD, + POST, + PUT, + DELETE, + } + + /// + /// Useful class for simulating the HttpContext. This does not actually + /// make an HttpRequest, it merely simulates the state that your code + /// would be in "as if" handling a request. Thus the HttpContext.Current + /// property is populated. + /// + public class HttpSimulator : IDisposable + { + private const string defaultPhysicalAppPath = @"c:\InetPub\wwwRoot\"; + private StringBuilder builder; + private Uri _referer; + private NameValueCollection _formVars = new NameValueCollection(); + private NameValueCollection _headers = new NameValueCollection(); + + public HttpSimulator() : this("/", defaultPhysicalAppPath) + { + } + + public HttpSimulator(string applicationPath) : this(applicationPath, defaultPhysicalAppPath) + { + + } + + public HttpSimulator(string applicationPath, string physicalApplicationPath) + { + this.ApplicationPath = applicationPath; + this.PhysicalApplicationPath = physicalApplicationPath; + } + + /// + /// Sets up the HttpContext objects to simulate a GET request. + /// + /// + /// Simulates a request to http://localhost/ + /// + public HttpSimulator SimulateRequest() + { + return SimulateRequest(new Uri("http://localhost/")); + } + + /// + /// Sets up the HttpContext objects to simulate a GET request. + /// + /// + public HttpSimulator SimulateRequest(Uri url) + { + return SimulateRequest(url, HttpVerb.GET); + } + + /// + /// Sets up the HttpContext objects to simulate a request. + /// + /// + /// + public HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb) + { + return SimulateRequest(url, httpVerb, null, null); + } + + /// + /// Sets up the HttpContext objects to simulate a POST request. + /// + /// + /// + public HttpSimulator SimulateRequest(Uri url, NameValueCollection formVariables) + { + return SimulateRequest(url, HttpVerb.POST, formVariables, null); + } + + /// + /// Sets up the HttpContext objects to simulate a POST request. + /// + /// + /// + /// + public HttpSimulator SimulateRequest(Uri url, NameValueCollection formVariables, NameValueCollection headers) + { + return SimulateRequest(url, HttpVerb.POST, formVariables, headers); + } + + /// + /// Sets up the HttpContext objects to simulate a request. + /// + /// + /// + /// + public HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, NameValueCollection headers) + { + return SimulateRequest(url, httpVerb, null, headers); + } + + /// + /// Sets up the HttpContext objects to simulate a request. + /// + /// + /// + /// + /// + protected virtual HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, NameValueCollection formVariables, NameValueCollection headers) + { + HttpContext.Current = null; + + ParseRequestUrl(url); + + if (this.responseWriter == null) + { + this.builder = new StringBuilder(); + this.responseWriter = new StringWriter(builder); + } + + SetHttpRuntimeInternals(); + + string query = ExtractQueryStringPart(url); + + if (formVariables != null) + _formVars.Add(formVariables); + + if (_formVars.Count > 0) + httpVerb = HttpVerb.POST; //Need to enforce this. + + if (headers != null) + _headers.Add(headers); + + var isSecure = url.AbsoluteUri.StartsWith("https:"); + + this.workerRequest = new SimulatedHttpRequest(ApplicationPath, PhysicalApplicationPath, PhysicalPath, Page, query, this.responseWriter, host, port, httpVerb.ToString(), isSecure); + this.workerRequest.Form.Add(_formVars); + this.workerRequest.Headers.Add(_headers); + + if (_referer != null) + this.workerRequest.SetReferer(_referer); + + InitializeSession(); + + InitializeApplication(); + + #region Console Debug INfo + + Console.WriteLine("host: " + host); + Console.WriteLine("virtualDir: " + applicationPath); + Console.WriteLine("page: " + localPath); + Console.WriteLine("pathPartAfterApplicationPart: " + _page); + Console.WriteLine("appPhysicalDir: " + physicalApplicationPath); + Console.WriteLine("Request.Url.LocalPath: " + HttpContext.Current.Request.Url.LocalPath); + Console.WriteLine("Request.Url.Host: " + HttpContext.Current.Request.Url.Host); + Console.WriteLine("Request.FilePath: " + HttpContext.Current.Request.FilePath); + Console.WriteLine("Request.Path: " + HttpContext.Current.Request.Path); + Console.WriteLine("Request.RawUrl: " + HttpContext.Current.Request.RawUrl); + Console.WriteLine("Request.Url: " + HttpContext.Current.Request.Url); + Console.WriteLine("Request.Url.Port: " + HttpContext.Current.Request.Url.Port); + Console.WriteLine("Request.ApplicationPath: " + HttpContext.Current.Request.ApplicationPath); + Console.WriteLine("Request.PhysicalPath: " + HttpContext.Current.Request.PhysicalPath); + Console.WriteLine("HttpRuntime.AppDomainAppPath: " + HttpRuntime.AppDomainAppPath); + Console.WriteLine("HttpRuntime.AppDomainAppVirtualPath: " + HttpRuntime.AppDomainAppVirtualPath); + Console.WriteLine("HostingEnvironment.ApplicationPhysicalPath: " + HostingEnvironment.ApplicationPhysicalPath); + Console.WriteLine("HostingEnvironment.ApplicationVirtualPath: " + HostingEnvironment.ApplicationVirtualPath); + + #endregion + + return this; + } + + private static void InitializeApplication() + { + Type appFactoryType = Type.GetType("System.Web.HttpApplicationFactory, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); + object appFactory = ReflectionHelper.GetStaticFieldValue("_theApplicationFactory", appFactoryType); + ReflectionHelper.SetPrivateInstanceFieldValue("_state", appFactory, HttpContext.Current.Application); + } + + private void InitializeSession() + { + HttpContext.Current = new HttpContext(workerRequest); + HttpContext.Current.Items.Clear(); + HttpSessionState session = (HttpSessionState)ReflectionHelper.Instantiate(typeof(HttpSessionState), new Type[] { typeof(IHttpSessionState) }, new FakeHttpSessionState()); + + HttpContext.Current.Items.Add("AspSession", session); + } + + public class FakeHttpSessionState : NameObjectCollectionBase, IHttpSessionState + { + private string sessionID = Guid.NewGuid().ToString(); + private int timeout = 30; //minutes + private bool isNewSession = true; + private int lcid; + private int codePage; + private HttpStaticObjectsCollection staticObjects = new HttpStaticObjectsCollection(); + private object syncRoot = new Object(); + + /// + ///Ends the current session. + /// + /// + public void Abandon() + { + BaseClear(); + } + + /// + ///Adds a new item to the session-state collection. + /// + /// + ///The name of the item to add to the session-state collection. + ///The value of the item to add to the session-state collection. + public void Add(string name, object value) + { + BaseAdd(name, value); + } + + /// + ///Deletes an item from the session-state item collection. + /// + /// + ///The name of the item to delete from the session-state item collection. + public void Remove(string name) + { + BaseRemove(name); + } + + /// + ///Deletes an item at a specified index from the session-state item collection. + /// + /// + ///The index of the item to remove from the session-state collection. + public void RemoveAt(int index) + { + BaseRemoveAt(index); + } + + /// + ///Clears all values from the session-state item collection. + /// + /// + public void Clear() + { + BaseClear(); + } + + /// + ///Clears all values from the session-state item collection. + /// + /// + public void RemoveAll() + { + BaseClear(); + } + + /// + ///Copies the collection of session-state item values to a one-dimensional array, starting at the specified index in the array. + /// + /// + ///The that receives the session values. + ///The index in array where copying starts. + public void CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + /// + ///Gets the unique session identifier for the session. + /// + /// + /// + ///The session ID. + /// + /// + public string SessionID + { + get { return sessionID; } + } + + /// + ///Gets and sets the time-out period (in minutes) allowed between requests before the session-state provider terminates the session. + /// + /// + /// + ///The time-out period, in minutes. + /// + /// + public int Timeout + { + get { return timeout; } + set { timeout = value; } + } + + /// + ///Gets a value indicating whether the session was created with the current request. + /// + /// + /// + ///true if the session was created with the current request; otherwise, false. + /// + /// + public bool IsNewSession + { + get { return isNewSession; } + } + + /// + ///Gets the current session-state mode. + /// + /// + /// + ///One of the values. + /// + /// + public SessionStateMode Mode + { + get { return SessionStateMode.InProc; } + } + + /// + ///Gets a value indicating whether the session ID is embedded in the URL or stored in an HTTP cookie. + /// + /// + /// + ///true if the session is embedded in the URL; otherwise, false. + /// + /// + public bool IsCookieless + { + get { return false; } + } + + /// + ///Gets a value that indicates whether the application is configured for cookieless sessions. + /// + /// + /// + ///One of the values that indicate whether the application is configured for cookieless sessions. The default is . + /// + /// + public HttpCookieMode CookieMode + { + get { return HttpCookieMode.UseCookies; } + } + + /// + ///Gets or sets the locale identifier (LCID) of the current session. + /// + /// + /// + ///A instance that specifies the culture of the current session. + /// + /// + public int LCID + { + get { return lcid; } + set { lcid = value; } + } + + /// + ///Gets or sets the code-page identifier for the current session. + /// + /// + /// + ///The code-page identifier for the current session. + /// + /// + public int CodePage + { + get { return codePage; } + set { codePage = value; } + } + + /// + ///Gets a collection of objects declared by <object Runat="Server" Scope="Session"/> tags within the ASP.NET application file Global.asax. + /// + /// + /// + ///An containing objects declared in the Global.asax file. + /// + /// + public HttpStaticObjectsCollection StaticObjects + { + get { return staticObjects; } + } + + /// + ///Gets or sets a session-state item value by name. + /// + /// + /// + ///The session-state item value specified in the name parameter. + /// + /// + ///The key name of the session-state item value. + public object this[string name] + { + get { return BaseGet(name); } + set { BaseSet(name, value); } + } + + /// + ///Gets or sets a session-state item value by numerical index. + /// + /// + /// + ///The session-state item value specified in the index parameter. + /// + /// + ///The numerical index of the session-state item value. + public object this[int index] + { + get { return BaseGet(index); } + set { BaseSet(index, value); } + } + + /// + ///Gets an object that can be used to synchronize access to the collection of session-state values. + /// + /// + /// + ///An object that can be used to synchronize access to the collection. + /// + /// + public object SyncRoot + { + get { return syncRoot; } + } + + + + /// + ///Gets a value indicating whether access to the collection of session-state values is synchronized (thread safe). + /// + /// + ///true if access to the collection is synchronized (thread safe); otherwise, false. + /// + /// + public bool IsSynchronized + { + get { return true; } + } + + /// + ///Gets a value indicating whether the session is read-only. + /// + /// + /// + ///true if the session is read-only; otherwise, false. + /// + /// + bool IHttpSessionState.IsReadOnly + { + get + { + return true; + } + } + } + + /// + /// Sets the referer for the request. Uses a fluent interface. + /// + /// + /// + public HttpSimulator SetReferer(Uri referer) + { + if (this.workerRequest != null) + this.workerRequest.SetReferer(referer); + this._referer = referer; + return this; + } + + /// + /// Sets a form variable. + /// + /// + /// + /// + public HttpSimulator SetFormVariable(string name, string value) + { + //TODO: Change this ordering requirement. + if (this.workerRequest != null) + throw new InvalidOperationException("Cannot set form variables after calling Simulate()."); + + _formVars.Add(name, value); + + return this; + } + + /// + /// Sets a header value. + /// + /// + /// + /// + public HttpSimulator SetHeader(string name, string value) + { + //TODO: Change this ordering requirement. + if (this.workerRequest != null) + throw new InvalidOperationException("Cannot set headers after calling Simulate()."); + + _headers.Add(name, value); + + return this; + } + + private void ParseRequestUrl(Uri url) + { + if (url == null) + return; + this.host = url.Host; + this.port = url.Port; + this.localPath = url.LocalPath; + this._page = StripPrecedingSlashes(RightAfter(url.LocalPath, ApplicationPath)); + this.physicalPath = Path.Combine(this.physicalApplicationPath, this._page.Replace("/", @"\")); + } + + static string RightAfter(string original, string search) + { + if (search.Length > original.Length || search.Length == 0) + return original; + + int searchIndex = original.IndexOf(search, 0, StringComparison.InvariantCultureIgnoreCase); + + if (searchIndex < 0) + return original; + + return original.Substring(original.IndexOf(search) + search.Length); + } + + public string Host + { + get { return this.host; } + } + + private string host; + + public string LocalPath + { + get { return this.localPath; } + } + + private string localPath; + + public int Port + { + get { return this.port; } + } + + private int port; + + /// + /// Portion of the URL after the application. + /// + public string Page + { + get { return this._page; } + } + + private string _page; + + /// + /// The same thing as the IIS Virtual directory. It's + /// what gets returned by Request.ApplicationPath. + /// + public string ApplicationPath + { + get { return this.applicationPath; } + set + { + this.applicationPath = value ?? "/"; + this.applicationPath = NormalizeSlashes(this.applicationPath); + } + } + private string applicationPath = "/"; + + /// + /// Physical path to the application (used for simulation purposes). + /// + public string PhysicalApplicationPath + { + get { return this.physicalApplicationPath; } + set + { + this.physicalApplicationPath = value ?? defaultPhysicalAppPath; + //strip trailing backslashes. + this.physicalApplicationPath = StripTrailingBackSlashes(this.physicalApplicationPath) + @"\"; + } + } + + private string physicalApplicationPath = defaultPhysicalAppPath; + + /// + /// Physical path to the requested file (used for simulation purposes). + /// + public string PhysicalPath + { + get { return this.physicalPath; } + } + + private string physicalPath = defaultPhysicalAppPath; + + public TextWriter ResponseWriter + { + get { return this.responseWriter; } + set { this.responseWriter = value; } + } + + /// + /// Returns the text from the response to the simulated request. + /// + public string ResponseText + { + get + { + return (builder ?? new StringBuilder()).ToString(); + } + } + + private TextWriter responseWriter; + + public SimulatedHttpRequest WorkerRequest + { + get { return this.workerRequest; } + } + + private SimulatedHttpRequest workerRequest; + + private static string ExtractQueryStringPart(Uri url) + { + string query = url.Query ?? string.Empty; + if (query.StartsWith("?")) + return query.Substring(1); + return query; + } + + void SetHttpRuntimeInternals() + { + //We cheat by using reflection. + + // get singleton property value + HttpRuntime runtime = ReflectionHelper.GetStaticFieldValue("_theRuntime", typeof(HttpRuntime)); + + // set app path property value + ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppPath", runtime, PhysicalApplicationPath); + // set app virtual path property value + string vpathTypeName = "System.Web.VirtualPath, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; + object virtualPath = ReflectionHelper.Instantiate(vpathTypeName, new Type[] { typeof(string) }, new object[] { ApplicationPath }); + ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppVPath", runtime, virtualPath); + + // set codegen dir property value + ReflectionHelper.SetPrivateInstanceFieldValue("_codegenDir", runtime, PhysicalApplicationPath); + + HostingEnvironment environment = GetHostingEnvironment(); + ReflectionHelper.SetPrivateInstanceFieldValue("_appPhysicalPath", environment, PhysicalApplicationPath); + ReflectionHelper.SetPrivateInstanceFieldValue("_appVirtualPath", environment, virtualPath); + ReflectionHelper.SetPrivateInstanceFieldValue("_configMapPath", environment, new ConfigMapPath(this)); + } + + protected static HostingEnvironment GetHostingEnvironment() + { + HostingEnvironment environment; + try + { + environment = new HostingEnvironment(); + } + catch (InvalidOperationException) + { + //Shoot, we need to grab it via reflection. + environment = ReflectionHelper.GetStaticFieldValue("_theHostingEnvironment", typeof(HostingEnvironment)); + } + return environment; + } + + #region --- Text Manipulation Methods for slashes --- + protected static string NormalizeSlashes(string s) + { + if (String.IsNullOrEmpty(s) || s == "/") + return "/"; + + s = s.Replace(@"\", "/"); + + //Reduce multiple slashes in row to single. + string normalized = Regex.Replace(s, "(/)/+", "$1"); + //Strip left. + normalized = StripPrecedingSlashes(normalized); + //Strip right. + normalized = StripTrailingSlashes(normalized); + return "/" + normalized; + } + + protected static string StripPrecedingSlashes(string s) + { + return Regex.Replace(s, "^/*(.*)", "$1"); + } + + protected static string StripTrailingSlashes(string s) + { + return Regex.Replace(s, "(.*)/*$", "$1", RegexOptions.RightToLeft); + } + + protected static string StripTrailingBackSlashes(string s) + { + if (String.IsNullOrEmpty(s)) + return string.Empty; + return Regex.Replace(s, @"(.*)\\*$", "$1", RegexOptions.RightToLeft); + } + #endregion + + internal class ConfigMapPath : IConfigMapPath + { + private HttpSimulator _requestSimulation; + public ConfigMapPath(HttpSimulator simulation) + { + _requestSimulation = simulation; + } + + public string GetMachineConfigFilename() + { + throw new NotImplementedException(); + } + + public string GetRootWebConfigFilename() + { + throw new NotImplementedException(); + } + + public void GetPathConfigFilename(string siteID, string path, out string directory, out string baseName) + { + throw new NotImplementedException(); + } + + public void GetDefaultSiteNameAndID(out string siteName, out string siteID) + { + throw new NotImplementedException(); + } + + public void ResolveSiteArgument(string siteArgument, out string siteName, out string siteID) + { + throw new NotImplementedException(); + } + + public string MapPath(string siteID, string path) + { + string page = StripPrecedingSlashes(RightAfter(path, _requestSimulation.ApplicationPath)); + return Path.Combine(_requestSimulation.PhysicalApplicationPath, page.Replace("/", @"\")); + } + + public string GetAppPathForPath(string siteID, string path) + { + return _requestSimulation.ApplicationPath; + } + } + + /// + ///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + ///2 + public void Dispose() + { + if (HttpContext.Current != null) + { + HttpContext.Current = null; + } + } + } } diff --git a/HttpSimulator/SimulatedHttpRequest.cs b/HttpSimulator/SimulatedHttpRequest.cs index e0a4267..84d40ca 100644 --- a/HttpSimulator/SimulatedHttpRequest.cs +++ b/HttpSimulator/SimulatedHttpRequest.cs @@ -21,200 +21,200 @@ namespace Subtext.TestLibrary { - /// - /// Used to simulate an HttpRequest. - /// - public class SimulatedHttpRequest : SimpleWorkerRequest - { - Uri _referer; - string _host; - string _verb; - private readonly bool _isSecure; - int _port; - string _physicalFilePath; - - /// - /// Creates a new instance. - /// - /// App virtual dir. - /// Physical Path to the app. - /// Physical Path to the file. - /// The Part of the URL after the application. - /// Query. - /// Output. - /// Host. - /// Port to request. - /// The HTTP Verb to use. - /// - public SimulatedHttpRequest(string applicationPath, string physicalAppPath, string physicalFilePath, string page, string query, TextWriter output, string host, int port, string verb, bool isSecure = false) : base(applicationPath, physicalAppPath, page, query, output) - { - if (host == null) - throw new ArgumentNullException("host", "Host cannot be null."); - - if (host.Length == 0) - throw new ArgumentException("Host cannot be empty.", "host"); - - if (applicationPath == null) - throw new ArgumentNullException("applicationPath", "Can't create a request with a null application path. Try empty string."); - - _host = host; - _verb = verb; - _isSecure = isSecure; - _port = port; - _physicalFilePath = physicalFilePath; - } - - public override bool IsSecure() - { - return _isSecure; - } - - internal void SetReferer(Uri referer) - { - _referer = referer; - } - - /// - /// Returns the specified member of the request header. - /// - /// - /// The HTTP verb returned in the request - /// header. - /// - public override string GetHttpVerbName() - { - return _verb; - } - - /// - /// Gets the name of the server. - /// - /// - public override string GetServerName() - { - return _host; - } - - public override int GetLocalPort() - { - return this._port; - } - - /// - /// Gets the headers. - /// - /// The headers. - public NameValueCollection Headers - { - get - { - return this.headers; - } - } - - private NameValueCollection headers = new NameValueCollection(); - - /// - /// Gets the format exception. - /// - /// The format exception. - public NameValueCollection Form - { - get - { - return formVariables; - } - } - private NameValueCollection formVariables = new NameValueCollection(); - - /// - /// Get all nonstandard HTTP header name-value pairs. - /// - /// An array of header name-value pairs. - public override string[][] GetUnknownRequestHeaders() - { - if (this.headers == null || this.headers.Count == 0) - { - return null; - } - string[][] headersArray = new string[this.headers.Count][]; - for (int i = 0; i < this.headers.Count; i++) - { - headersArray[i] = new string[2]; - headersArray[i][0] = this.headers.Keys[i]; - headersArray[i][1] = this.headers[i]; - } - return headersArray; - } - - public override string GetKnownRequestHeader(int index) - { - if (index == 0x24) - return _referer == null ? string.Empty : _referer.ToString(); - - if (index == 12 && this._verb == "POST") - return "application/x-www-form-urlencoded"; - - return base.GetKnownRequestHeader(index); - } - - /// - /// Returns the virtual path to the currently executing - /// server application. - /// - /// - /// The virtual path of the current application. - /// - public override string GetAppPath() - { - string appPath = base.GetAppPath(); - return appPath; - } - - public override string GetAppPathTranslated() - { - string path = base.GetAppPathTranslated(); - return path; - } - - public override string GetUriPath() - { - string uriPath = base.GetUriPath(); - return uriPath; - } - - public override string GetFilePathTranslated() - { - return _physicalFilePath; - } - - /// - /// Reads request data from the client (when not preloaded). - /// - /// The number of bytes read. - public override byte[] GetPreloadedEntityBody() - { - string formText = string.Empty; - - foreach (string key in this.formVariables.Keys) - { - formText += string.Format("{0}={1}&", key, this.formVariables[key]); - } - - return Encoding.UTF8.GetBytes(formText); - } - - /// - /// Returns a value indicating whether all request data - /// is available and no further reads from the client are required. - /// - /// - /// if all request data is available; otherwise, - /// . - /// - public override bool IsEntireEntityBodyIsPreloaded() - { - return true; - } - } + /// + /// Used to simulate an HttpRequest. + /// + public class SimulatedHttpRequest : SimpleWorkerRequest + { + Uri _referer; + string _host; + string _verb; + private readonly bool _isSecure; + int _port; + string _physicalFilePath; + + /// + /// Creates a new instance. + /// + /// App virtual dir. + /// Physical Path to the app. + /// Physical Path to the file. + /// The Part of the URL after the application. + /// Query. + /// Output. + /// Host. + /// Port to request. + /// The HTTP Verb to use. + /// + public SimulatedHttpRequest(string applicationPath, string physicalAppPath, string physicalFilePath, string page, string query, TextWriter output, string host, int port, string verb, bool isSecure = false) : base(applicationPath, physicalAppPath, page, query, output) + { + if (host == null) + throw new ArgumentNullException("host", "Host cannot be null."); + + if (host.Length == 0) + throw new ArgumentException("Host cannot be empty.", "host"); + + if (applicationPath == null) + throw new ArgumentNullException("applicationPath", "Can't create a request with a null application path. Try empty string."); + + _host = host; + _verb = verb; + _isSecure = isSecure; + _port = port; + _physicalFilePath = physicalFilePath; + } + + public override bool IsSecure() + { + return _isSecure; + } + + internal void SetReferer(Uri referer) + { + _referer = referer; + } + + /// + /// Returns the specified member of the request header. + /// + /// + /// The HTTP verb returned in the request + /// header. + /// + public override string GetHttpVerbName() + { + return _verb; + } + + /// + /// Gets the name of the server. + /// + /// + public override string GetServerName() + { + return _host; + } + + public override int GetLocalPort() + { + return this._port; + } + + /// + /// Gets the headers. + /// + /// The headers. + public NameValueCollection Headers + { + get + { + return this.headers; + } + } + + private NameValueCollection headers = new NameValueCollection(); + + /// + /// Gets the format exception. + /// + /// The format exception. + public NameValueCollection Form + { + get + { + return formVariables; + } + } + private NameValueCollection formVariables = new NameValueCollection(); + + /// + /// Get all nonstandard HTTP header name-value pairs. + /// + /// An array of header name-value pairs. + public override string[][] GetUnknownRequestHeaders() + { + if (this.headers == null || this.headers.Count == 0) + { + return null; + } + string[][] headersArray = new string[this.headers.Count][]; + for (int i = 0; i < this.headers.Count; i++) + { + headersArray[i] = new string[2]; + headersArray[i][0] = this.headers.Keys[i]; + headersArray[i][1] = this.headers[i]; + } + return headersArray; + } + + public override string GetKnownRequestHeader(int index) + { + if (index == 0x24) + return _referer == null ? string.Empty : _referer.ToString(); + + if (index == 12 && this._verb == "POST") + return "application/x-www-form-urlencoded"; + + return base.GetKnownRequestHeader(index); + } + + /// + /// Returns the virtual path to the currently executing + /// server application. + /// + /// + /// The virtual path of the current application. + /// + public override string GetAppPath() + { + string appPath = base.GetAppPath(); + return appPath; + } + + public override string GetAppPathTranslated() + { + string path = base.GetAppPathTranslated(); + return path; + } + + public override string GetUriPath() + { + string uriPath = base.GetUriPath(); + return uriPath; + } + + public override string GetFilePathTranslated() + { + return _physicalFilePath; + } + + /// + /// Reads request data from the client (when not preloaded). + /// + /// The number of bytes read. + public override byte[] GetPreloadedEntityBody() + { + string formText = string.Empty; + + foreach (string key in this.formVariables.Keys) + { + formText += string.Format("{0}={1}&", key, this.formVariables[key]); + } + + return Encoding.UTF8.GetBytes(formText); + } + + /// + /// Returns a value indicating whether all request data + /// is available and no further reads from the client are required. + /// + /// + /// if all request data is available; otherwise, + /// . + /// + public override bool IsEntireEntityBodyIsPreloaded() + { + return true; + } + } }