diff --git a/HttpSimulator/HttpSimulator.cs b/HttpSimulator/HttpSimulator.cs index 86fcdac..0ed2df9 100644 --- a/HttpSimulator/HttpSimulator.cs +++ b/HttpSimulator/HttpSimulator.cs @@ -19,7 +19,7 @@ public enum HttpVerb 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 @@ -39,7 +39,7 @@ public HttpSimulator() : this("/", defaultPhysicalAppPath) public HttpSimulator(string applicationPath) : this(applicationPath, defaultPhysicalAppPath) { - + } public HttpSimulator(string applicationPath, string physicalApplicationPath) @@ -141,19 +141,20 @@ protected virtual HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, Name if (headers != null) _headers.Add(headers); - - this.workerRequest = new SimulatedHttpRequest(ApplicationPath, PhysicalApplicationPath, PhysicalPath, Page, query, this.responseWriter, host, port, httpVerb.ToString()); + 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(); + InitializeSession(); + + InitializeApplication(); - InitializeApplication(); - #region Console Debug INfo Console.WriteLine("host: " + host); @@ -176,307 +177,307 @@ protected virtual HttpSimulator SimulateRequest(Uri url, HttpVerb httpVerb, Name 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; - } - } - } - - /// + 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) + if (this.workerRequest != null) this.workerRequest.SetReferer(referer); this._referer = referer; return this; @@ -523,24 +524,24 @@ private void ParseRequestUrl(Uri url) this.host = url.Host; this.port = url.Port; this.localPath = url.LocalPath; - this._page = StripPrecedingSlashes(RightAfter(url.LocalPath, ApplicationPath)); + 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; + 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); + int searchIndex = original.IndexOf(search, 0, StringComparison.InvariantCultureIgnoreCase); - if (searchIndex < 0) - return original; + if (searchIndex < 0) + return original; - return original.Substring(original.IndexOf(search) + search.Length); - } + return original.Substring(original.IndexOf(search) + search.Length); + } - public string Host + public string Host { get { return this.host; } } @@ -553,7 +554,7 @@ public string LocalPath } private string localPath; - + public int Port { get { return this.port; } @@ -578,8 +579,8 @@ public string Page public string ApplicationPath { get { return this.applicationPath; } - set - { + set + { this.applicationPath = value ?? "/"; this.applicationPath = NormalizeSlashes(this.applicationPath); } @@ -592,7 +593,7 @@ public string ApplicationPath public string PhysicalApplicationPath { get { return this.physicalApplicationPath; } - set + set { this.physicalApplicationPath = value ?? defaultPhysicalAppPath; //strip trailing backslashes. @@ -641,7 +642,7 @@ public SimulatedHttpRequest WorkerRequest private static string ExtractQueryStringPart(Uri url) { string query = url.Query ?? string.Empty; - if(query.StartsWith("?")) + if (query.StartsWith("?")) return query.Substring(1); return query; } @@ -651,8 +652,8 @@ void SetHttpRuntimeInternals() //We cheat by using reflection. // get singleton property value - HttpRuntime runtime = ReflectionHelper.GetStaticFieldValue("_theRuntime", typeof (HttpRuntime)); - + HttpRuntime runtime = ReflectionHelper.GetStaticFieldValue("_theRuntime", typeof(HttpRuntime)); + // set app path property value ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppPath", runtime, PhysicalApplicationPath); // set app virtual path property value @@ -770,7 +771,7 @@ public string GetAppPathForPath(string siteID, string path) ///2 public void Dispose() { - if(HttpContext.Current != null) + if (HttpContext.Current != null) { HttpContext.Current = null; } diff --git a/HttpSimulator/SimulatedHttpRequest.cs b/HttpSimulator/SimulatedHttpRequest.cs index b013db4..84d40ca 100644 --- a/HttpSimulator/SimulatedHttpRequest.cs +++ b/HttpSimulator/SimulatedHttpRequest.cs @@ -21,16 +21,17 @@ namespace Subtext.TestLibrary { - /// - /// Used to simulate an HttpRequest. - /// - public class SimulatedHttpRequest : SimpleWorkerRequest - { - Uri _referer; - string _host; - string _verb; + /// + /// Used to simulate an HttpRequest. + /// + public class SimulatedHttpRequest : SimpleWorkerRequest + { + Uri _referer; + string _host; + string _verb; + private readonly bool _isSecure; int _port; - string _physicalFilePath; + string _physicalFilePath; /// /// Creates a new instance. @@ -44,12 +45,13 @@ public class SimulatedHttpRequest : SimpleWorkerRequest /// 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) - { + /// + 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) + if (host.Length == 0) throw new ArgumentException("Host cannot be empty.", "host"); if (applicationPath == null) @@ -57,111 +59,117 @@ public SimulatedHttpRequest(string applicationPath, string physicalAppPath, stri _host = host; _verb = verb; - _port = port; + _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() - { + /// + /// 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; - } + } + + /// + /// 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(); + 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; - } + /// + /// 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() { @@ -179,34 +187,34 @@ 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; - } - } + + /// + /// 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; + } + } }