diff options
Diffstat (limited to 'src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs')
-rw-r--r-- | src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs | 122 |
1 files changed, 114 insertions, 8 deletions
diff --git a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs index 2bd09ec..cfc122d 100644 --- a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs +++ b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.Messaging { using System; using System.Collections.Specialized; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net; @@ -34,6 +35,11 @@ namespace DotNetOpenAuth.Messaging { private NameValueCollection queryString; /// <summary> + /// Backing field for the <see cref="QueryStringBeforeRewriting"/> property. + /// </summary> + private NameValueCollection queryStringBeforeRewriting; + + /// <summary> /// Backing field for the <see cref="Message"/> property. /// </summary> private IDirectedProtocolMessage message; @@ -43,12 +49,12 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="request">The ASP.NET structure to copy from.</param> public HttpRequestInfo(HttpRequest request) { - if (request == null) { - throw new ArgumentNullException("request"); - } + Contract.Requires(request != null); + ErrorUtilities.VerifyArgumentNotNull(request, "request"); this.HttpMethod = request.HttpMethod; this.Url = request.Url; + this.RawUrl = request.RawUrl; this.Headers = GetHeaderCollection(request.Headers); this.InputStream = request.InputStream; @@ -65,15 +71,23 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="httpMethod">The HTTP method (i.e. GET or POST) of the incoming request.</param> /// <param name="requestUrl">The URL being requested.</param> + /// <param name="rawUrl">The raw URL that appears immediately following the HTTP verb in the request, + /// before any URL rewriting takes place.</param> /// <param name="headers">Headers in the HTTP request.</param> /// <param name="inputStream">The entity stream, if any. (POST requests typically have these). Use <c>null</c> for GET requests.</param> - public HttpRequestInfo(string httpMethod, Uri requestUrl, WebHeaderCollection headers, Stream inputStream) { + public HttpRequestInfo(string httpMethod, Uri requestUrl, string rawUrl, WebHeaderCollection headers, Stream inputStream) { + Contract.Requires(!string.IsNullOrEmpty(httpMethod)); + Contract.Requires(requestUrl != null); + Contract.Requires(rawUrl != null); + Contract.Requires(headers != null); ErrorUtilities.VerifyNonZeroLength(httpMethod, "httpMethod"); ErrorUtilities.VerifyArgumentNotNull(requestUrl, "requestUrl"); + ErrorUtilities.VerifyArgumentNotNull(rawUrl, "rawUrl"); ErrorUtilities.VerifyArgumentNotNull(headers, "headers"); this.HttpMethod = httpMethod; this.Url = requestUrl; + this.RawUrl = rawUrl; this.Headers = headers; this.InputStream = inputStream; } @@ -83,10 +97,12 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="listenerRequest">Details on the incoming HTTP request.</param> public HttpRequestInfo(HttpListenerRequest listenerRequest) { + Contract.Requires(listenerRequest != null); ErrorUtilities.VerifyArgumentNotNull(listenerRequest, "listenerRequest"); this.HttpMethod = listenerRequest.HttpMethod; this.Url = listenerRequest.Url; + this.RawUrl = listenerRequest.RawUrl; this.Headers = new WebHeaderCollection(); foreach (string key in listenerRequest.Headers) { this.Headers[key] = listenerRequest.Headers[key]; @@ -101,13 +117,15 @@ namespace DotNetOpenAuth.Messaging { /// <param name="request">The WCF incoming request structure to get the HTTP information from.</param> /// <param name="requestUri">The URI of the service endpoint.</param> public HttpRequestInfo(HttpRequestMessageProperty request, Uri requestUri) { - if (request == null) { - throw new ArgumentNullException("request"); - } + Contract.Requires(request != null); + Contract.Requires(requestUri != null); + ErrorUtilities.VerifyArgumentNotNull(request, "request"); + ErrorUtilities.VerifyArgumentNotNull(requestUri, "requestUri"); this.HttpMethod = request.Method; this.Headers = request.Headers; this.Url = requestUri; + this.RawUrl = MakeUpRawUrlFromUrl(requestUri); } /// <summary> @@ -123,8 +141,12 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="request">The HttpWebRequest (that was never used) to copy from.</param> internal HttpRequestInfo(WebRequest request) { + Contract.Requires(request != null); + ErrorUtilities.VerifyArgumentNotNull(request, "request"); + this.HttpMethod = request.Method; this.Url = request.RequestUri; + this.RawUrl = MakeUpRawUrlFromUrl(request.RequestUri); this.Headers = GetHeaderCollection(request.Headers); this.InputStream = null; } @@ -157,11 +179,36 @@ namespace DotNetOpenAuth.Messaging { internal string HttpMethod { get; set; } /// <summary> - /// Gets or sets the entire URL of the request. + /// Gets or sets the entire URL of the request, after any URL rewriting. /// </summary> internal Uri Url { get; set; } /// <summary> + /// Gets or sets the raw URL that appears immediately following the HTTP verb in the request, + /// before any URL rewriting takes place. + /// </summary> + internal string RawUrl { get; set; } + + /// <summary> + /// Gets the full URL of a request before rewriting. + /// </summary> + internal Uri UrlBeforeRewriting { + get { + Contract.Requires(this.Url != null); + Contract.Requires(this.RawUrl != null); + + // We use Request.Url for the full path to the server, and modify it + // with Request.RawUrl to capture both the cookieless session "directory" if it exists + // and the original path in case URL rewriting is going on. We don't want to be + // fooled by URL rewriting because we're comparing the actual URL with what's in + // the return_to parameter in some cases. + // Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless + // session, but not the URL rewriting problem. + return new Uri(this.Url, this.RawUrl); + } + } + + /// <summary> /// Gets the query part of the URL (The ? and everything after it). /// </summary> internal string Query { @@ -215,6 +262,65 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Gets the query data from the original request (before any URL rewriting has occurred.) + /// </summary> + /// <returns>A <see cref="NameValueCollection"/> containing all the parameters in the query string.</returns> + internal NameValueCollection QueryStringBeforeRewriting { + get { + if (this.queryStringBeforeRewriting == null) { + // This request URL may have been rewritten by the host site. + // For openid protocol purposes, we really need to look at + // the original query parameters before any rewriting took place. + if (!this.IsUrlRewritten) { + // No rewriting has taken place. + this.queryStringBeforeRewriting = this.QueryString; + } else { + // Rewriting detected! Recover the original request URI. + this.queryStringBeforeRewriting = HttpUtility.ParseQueryString(this.UrlBeforeRewriting.Query); + } + } + + return this.queryStringBeforeRewriting; + } + } + + /// <summary> + /// Gets a value indicating whether the request's URL was rewritten by ASP.NET + /// or some other module. + /// </summary> + /// <value> + /// <c>true</c> if this request's URL was rewritten; otherwise, <c>false</c>. + /// </value> + internal bool IsUrlRewritten { + get { return this.Url.PathAndQuery != this.RawUrl; } + } + + /// <summary> + /// Gets the query or form data from the original request (before any URL rewriting has occurred.) + /// </summary> + /// <returns>A set of name=value pairs.</returns> + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call")] + internal NameValueCollection GetQueryOrFormFromContext() { + NameValueCollection query; + if (this.HttpMethod == "GET") { + query = this.QueryStringBeforeRewriting; + } else { + query = this.Form; + } + return query; + } + + /// <summary> + /// Makes up a reasonable guess at the raw URL from the possibly rewritten URL. + /// </summary> + /// <param name="url">A full URL.</param> + /// <returns>A raw URL that might have come in on the HTTP verb.</returns> + private static string MakeUpRawUrlFromUrl(Uri url) { + Contract.Requires(url != null); + return url.AbsolutePath + url.Query + url.Fragment; + } + + /// <summary> /// Converts a NameValueCollection to a WebHeaderCollection. /// </summary> /// <param name="pairs">The collection a HTTP headers.</param> |