summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2008-11-26 07:53:56 -0800
committerAndrew <andrewarnott@gmail.com>2008-11-26 07:53:56 -0800
commit98f10d7920e2d957f6aff937b9dd253f84145668 (patch)
tree1a51ab400ac3e5ae6b97a63812d2f014f5724377 /src
parentdbb8e6cf3329dfee52fa78a19026134eaae2bae7 (diff)
downloadDotNetOpenAuth-98f10d7920e2d957f6aff937b9dd253f84145668.zip
DotNetOpenAuth-98f10d7920e2d957f6aff937b9dd253f84145668.tar.gz
DotNetOpenAuth-98f10d7920e2d957f6aff937b9dd253f84145668.tar.bz2
Refactored the web request handler classes.
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth/Messaging/ErrorUtilities.cs10
-rw-r--r--src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs14
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs9
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.resx3
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs69
-rw-r--r--src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs67
-rw-r--r--src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs249
7 files changed, 259 insertions, 162 deletions
diff --git a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
index 552662c..4256414 100644
--- a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
@@ -67,8 +67,16 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="message">The message to set in the exception.</param>
/// <param name="args">The formatting arguments of the message.</param>
- internal static void ThrowProtocol(string message, params object[] args) {
+ /// <returns>
+ /// An InternalErrorException, which may be "thrown" by the caller in order
+ /// to satisfy C# rules to show that code will never be reached, but no value
+ /// actually is ever returned because this method guarantees to throw.
+ /// </returns>
+ internal static Exception ThrowProtocol(string message, params object[] args) {
VerifyProtocol(false, message, args);
+
+ // we never reach here, but this allows callers to "throw" this method.
+ return new InternalErrorException();
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
index 51513db..d355a31 100644
--- a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
@@ -23,6 +23,14 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>
/// The writer the caller should write out the entity data to.
/// </returns>
+ /// <exception cref="ProtocolException">Thrown for any network error.</exception>
+ /// <remarks>
+ /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
+ /// and any other appropriate properties <i>before</i> calling this method.</para>
+ /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
+ /// <see cref="ProtocolException"/> to abstract away the transport and provide
+ /// a single exception type for hosts to catch.</para>
+ /// </remarks>
TextWriter GetRequestStream(HttpWebRequest request);
/// <summary>
@@ -31,6 +39,12 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
/// <returns>An instance of <see cref="DirectWebResponse"/> describing the response.</returns>
+ /// <exception cref="ProtocolException">Thrown for any network error.</exception>
+ /// <remarks>
+ /// Implementations should catch <see cref="WebException"/> and wrap it in a
+ /// <see cref="ProtocolException"/> to abstract away the transport and provide
+ /// a single exception type for hosts to catch.
+ /// </remarks>
DirectWebResponse GetResponse(HttpWebRequest request);
}
}
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
index 7bd3d94..d58ca40 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
@@ -475,6 +475,15 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Looks up a localized string similar to Redirects on POST requests that are to untrusted servers is not supported..
+ /// </summary>
+ internal static string UntrustedRedirectsOnPOSTNotSupported {
+ get {
+ return ResourceManager.GetString("UntrustedRedirectsOnPOSTNotSupported", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Web request to &apos;{0}&apos; failed..
/// </summary>
internal static string WebRequestFailed {
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
index 767b07f..35ba79c 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
@@ -255,6 +255,9 @@
<data name="UnsafeWebRequestDetected" xml:space="preserve">
<value>The URL '{0}' is rated unsafe and cannot be requested this way.</value>
</data>
+ <data name="UntrustedRedirectsOnPOSTNotSupported" xml:space="preserve">
+ <value>Redirects on POST requests that are to untrusted servers is not supported.</value>
+ </data>
<data name="WebRequestFailed" xml:space="preserve">
<value>Web request to '{0}' failed.</value>
</data>
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index 0964010..d008206 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -169,6 +169,75 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Clones an <see cref="HttpWebRequest"/> in order to send it again.
+ /// </summary>
+ /// <param name="request">The request to clone.</param>
+ /// <returns>The newly created instance.</returns>
+ internal static HttpWebRequest Clone(this HttpWebRequest request) {
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ return Clone(request, request.RequestUri);
+ }
+
+ /// <summary>
+ /// Clones an <see cref="HttpWebRequest"/> in order to send it again.
+ /// </summary>
+ /// <param name="request">The request to clone.</param>
+ /// <param name="newRequestUri">The new recipient of the request.</param>
+ /// <returns>The newly created instance.</returns>
+ internal static HttpWebRequest Clone(this HttpWebRequest request, Uri newRequestUri) {
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ ErrorUtilities.VerifyArgumentNotNull(newRequestUri, "newRequestUri");
+
+ var newRequest = (HttpWebRequest)WebRequest.Create(newRequestUri);
+ newRequest.Accept = request.Accept;
+ newRequest.AllowAutoRedirect = request.AllowAutoRedirect;
+ newRequest.AllowWriteStreamBuffering = request.AllowWriteStreamBuffering;
+ newRequest.AuthenticationLevel = request.AuthenticationLevel;
+ newRequest.AutomaticDecompression = request.AutomaticDecompression;
+ newRequest.CachePolicy = request.CachePolicy;
+ newRequest.ClientCertificates = request.ClientCertificates;
+ newRequest.ConnectionGroupName = request.ConnectionGroupName;
+ if (request.ContentLength >= 0) {
+ newRequest.ContentLength = request.ContentLength;
+ }
+ newRequest.ContentType = request.ContentType;
+ newRequest.ContinueDelegate = request.ContinueDelegate;
+ newRequest.CookieContainer = request.CookieContainer;
+ newRequest.Credentials = request.Credentials;
+ newRequest.Expect = request.Expect;
+ newRequest.IfModifiedSince = request.IfModifiedSince;
+ newRequest.ImpersonationLevel = request.ImpersonationLevel;
+ newRequest.KeepAlive = request.KeepAlive;
+ newRequest.MaximumAutomaticRedirections = request.MaximumAutomaticRedirections;
+ newRequest.MaximumResponseHeadersLength = request.MaximumResponseHeadersLength;
+ newRequest.MediaType = request.MediaType;
+ newRequest.Method = request.Method;
+ newRequest.Pipelined = request.Pipelined;
+ newRequest.PreAuthenticate = request.PreAuthenticate;
+ newRequest.ProtocolVersion = request.ProtocolVersion;
+ newRequest.Proxy = request.Proxy;
+ newRequest.ReadWriteTimeout = request.ReadWriteTimeout;
+ newRequest.Referer = request.Referer;
+ newRequest.SendChunked = request.SendChunked;
+ newRequest.Timeout = request.Timeout;
+ newRequest.TransferEncoding = request.TransferEncoding;
+ newRequest.UnsafeAuthenticatedConnectionSharing = request.UnsafeAuthenticatedConnectionSharing;
+ newRequest.UseDefaultCredentials = request.UseDefaultCredentials;
+ newRequest.UserAgent = request.UserAgent;
+
+ // We copy headers last, and only those that do not yet exist as a result
+ // of setting these properties, so as to avoid exceptions thrown because
+ // there are properties .NET wants us to use rather than direct headers.
+ foreach (string header in request.Headers) {
+ if (string.IsNullOrEmpty(newRequest.Headers[header])) {
+ newRequest.Headers.Add(header, request.Headers[header]);
+ }
+ }
+
+ return newRequest;
+ }
+
+ /// <summary>
/// Tests whether two arrays are equal in length and contents.
/// </summary>
/// <typeparam name="T">The type of elements in the arrays.</typeparam>
diff --git a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
index 8161de9..a82b936 100644
--- a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
@@ -18,27 +18,40 @@ namespace DotNetOpenAuth.Messaging {
#region IWebRequestHandler Members
/// <summary>
- /// Prepares a POST <see cref="HttpWebRequest"/> and returns the request stream
- /// for writing out the POST entity data.
+ /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
/// </summary>
/// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
- /// <returns>The stream the caller should write out the entity data to.</returns>
+ /// <returns>
+ /// The writer the caller should write out the entity data to.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown for any network error.</exception>
+ /// <remarks>
+ /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
+ /// and any other appropriate properties <i>before</i> calling this method.</para>
+ /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
+ /// <see cref="ProtocolException"/> to abstract away the transport and provide
+ /// a single exception type for hosts to catch.</para>
+ /// </remarks>
public TextWriter GetRequestStream(HttpWebRequest request) {
ErrorUtilities.VerifyArgumentNotNull(request, "request");
- try {
- return new StreamWriter(request.GetRequestStream());
- } catch (WebException ex) {
- throw new ProtocolException(MessagingStrings.ErrorInRequestReplyMessage, ex);
- }
+ return GetRequestStreamCore(request);
}
/// <summary>
- /// Processes an <see cref="HttpWebRequest"/> and converts the
+ /// Processes an <see cref="HttpWebRequest"/> and converts the
/// <see cref="HttpWebResponse"/> to a <see cref="DirectWebResponse"/> instance.
/// </summary>
/// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
- /// <returns>An instance of <see cref="DirectWebResponse"/> describing the response.</returns>
+ /// <returns>
+ /// An instance of <see cref="DirectWebResponse"/> describing the response.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown for any network error.</exception>
+ /// <remarks>
+ /// Implementations should catch <see cref="WebException"/> and wrap it in a
+ /// <see cref="ProtocolException"/> to abstract away the transport and provide
+ /// a single exception type for hosts to catch.
+ /// </remarks>
public DirectWebResponse GetResponse(HttpWebRequest request) {
ErrorUtilities.VerifyArgumentNotNull(request, "request");
@@ -57,10 +70,42 @@ namespace DotNetOpenAuth.Messaging {
Logger.ErrorFormat("WebException {1} from {0}, no response available.", request.RequestUri, ex.Status);
}
}
- throw new ProtocolException(MessagingStrings.ErrorInRequestReplyMessage, ex);
+
+ throw ErrorUtilities.Wrap(ex, MessagingStrings.ErrorInRequestReplyMessage);
}
}
#endregion
+
+ /// <summary>
+ /// Initiates a POST request and prepares for sending data.
+ /// </summary>
+ /// <param name="request">The HTTP request with information about the remote party to contact.</param>
+ /// <returns>The stream where the POST entity can be written.</returns>
+ private static TextWriter GetRequestStreamCore(HttpWebRequest request) {
+ try {
+ return new StreamWriter(request.GetRequestStream());
+ } catch (WebException ex) {
+ using (HttpWebResponse response = (HttpWebResponse)ex.Response) {
+ if (response != null && response.StatusCode == HttpStatusCode.ExpectationFailed &&
+ request.ServicePoint.Expect100Continue) {
+ // Some OpenID servers doesn't understand the Expect header and send 417 error back.
+ // If this server just failed from that, we're trying again without sending the
+ // "Expect: 100-Continue" HTTP header. (see Google Code Issue 72)
+ // We don't just set Expect100Continue = !avoidSendingExpect100Continue
+ // so that future requests don't reset this and have to try twice as well.
+ // We don't want to blindly set all ServicePoints to not use the Expect header
+ // as that would be a security hole allowing any visitor to a web site change
+ // the web site's global behavior when calling that host.
+ request = request.Clone();
+ request.ServicePoint.Expect100Continue = false; // TODO: investigate that CAS may throw here, and we can use request.Expect instead.
+ // request.Expect = ""; // alternative to ServicePoint if we don't have permission to set that, but be sure to change the if clause above if we use this.
+ return GetRequestStreamCore(request);
+ } else {
+ throw ErrorUtilities.Wrap(ex, MessagingStrings.WebRequestFailed, request.RequestUri);
+ }
+ }
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
index 9a196e1..5678cc4 100644
--- a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
@@ -40,39 +40,47 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
internal static readonly RequestCachePolicy DefaultCachePolicy = HttpWebRequest.DefaultCachePolicy;
- private ICollection<Regex> blacklistHostsRegex = new List<Regex>(Configuration.BlacklistHostsRegex.KeysAsRegexs);
-
+ /// <summary>
+ /// The set of URI schemes allowed in untrusted web requests.
+ /// </summary>
private ICollection<string> allowableSchemes = new List<string> { "http", "https" };
- private ICollection<Regex> whitelistHostsRegex = new List<Regex>(Configuration.WhitelistHostsRegex.KeysAsRegexs);
+ /// <summary>
+ /// The collection of blacklisted hosts.
+ /// </summary>
+ private ICollection<string> blacklistHosts = new List<string>(Configuration.BlacklistHosts.KeysAsStrings);
+ /// <summary>
+ /// The collection of regular expressions used to identify additional blacklisted hosts.
+ /// </summary>
+ private ICollection<Regex> blacklistHostsRegex = new List<Regex>(Configuration.BlacklistHostsRegex.KeysAsRegexs);
+
+ /// <summary>
+ /// The collection of whitelisted hosts.
+ /// </summary>
private ICollection<string> whitelistHosts = new List<string>(Configuration.WhitelistHosts.KeysAsStrings);
- private ICollection<string> blacklistHosts = new List<string>(Configuration.BlacklistHosts.KeysAsStrings);
+ /// <summary>
+ /// The collection of regular expressions used to identify additional whitelisted hosts.
+ /// </summary>
+ private ICollection<Regex> whitelistHostsRegex = new List<Regex>(Configuration.WhitelistHostsRegex.KeysAsRegexs);
+ /// <summary>
+ /// The maximum redirections to follow in the course of a single request.
+ /// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private int maximumRedirections = Configuration.MaximumRedirections;
+ /// <summary>
+ /// The maximum number of bytes to read from the response of an untrusted server.
+ /// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private int maximumBytesToRead = Configuration.MaximumBytesToRead;
/// <summary>
- /// Gets or sets the default maximum bytes to read in any given HTTP request.
+ /// The handler that will actually send the HTTP request and collect
+ /// the response once the untrusted server gates have been satisfied.
/// </summary>
- /// <value>Default is 1MB. Cannot be less than 2KB.</value>
- public int MaximumBytesToRead {
- get {
- return this.maximumBytesToRead;
- }
-
- set {
- if (value < 2048) {
- throw new ArgumentOutOfRangeException("value");
- }
- this.maximumBytesToRead = value;
- }
- }
-
private IDirectWebRequestHandler chainedWebRequestHandler;
/// <summary>
@@ -99,6 +107,23 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Gets or sets the default maximum bytes to read in any given HTTP request.
+ /// </summary>
+ /// <value>Default is 1MB. Cannot be less than 2KB.</value>
+ public int MaximumBytesToRead {
+ get {
+ return this.maximumBytesToRead;
+ }
+
+ set {
+ if (value < 2048) {
+ throw new ArgumentOutOfRangeException("value");
+ }
+ this.maximumBytesToRead = value;
+ }
+ }
+
+ /// <summary>
/// Gets or sets the total number of redirections to allow on any one request.
/// Default is 10.
/// </summary>
@@ -147,10 +172,13 @@ namespace DotNetOpenAuth.Messaging {
/// <summary>
/// Gets a collection of host name regular expressions that indicate hosts that should
- /// be rjected even if they pass standard security checks.
+ /// be rejected even if they pass standard security checks.
/// </summary>
public ICollection<Regex> BlacklistHostsRegex { get { return this.blacklistHostsRegex; } }
+ /// <summary>
+ /// Gets the configuration for this class that is specified in the host's .config file.
+ /// </summary>
private static DotNetOpenAuth.Configuration.UntrustedWebRequestSection Configuration {
get { return UntrustedWebRequestSection.Configuration; }
}
@@ -171,11 +199,6 @@ namespace DotNetOpenAuth.Messaging {
this.PrepareRequest(request);
- // We don't currently support redirects at URLs where we're POSTing data.
- // When we want to add this support, we need to be careful to not allow
- // redirects to non-HTTPS schemes if RequireSsl is true.
- request.AllowAutoRedirect = false;
-
// Submit the request and get the request stream back.
return this.chainedWebRequestHandler.GetRequestStream(request);
}
@@ -191,13 +214,38 @@ namespace DotNetOpenAuth.Messaging {
/// </returns>
public DirectWebResponse GetResponse(HttpWebRequest request, bool requireSsl) {
ErrorUtilities.VerifyArgumentNotNull(request, "request");
- this.EnsureAllowableRequestUri(request.RequestUri, requireSsl);
// This request MAY have already been prepared by GetRequestStream, but
// we have no guarantee, so do it just to be safe.
this.PrepareRequest(request);
- return this.RequestWithManagedRedirects(request, requireSsl);
+ // Since we may require SSL for every redirect, we handle each redirect manually
+ // in order to detect and fail if any redirect sends us to an HTTP url.
+ // We COULD allow automatic redirect in the cases where HTTPS is not required,
+ // but our mock request infrastructure can't do redirects on its own either.
+ Uri originalRequestUri = request.RequestUri;
+ int i;
+ for (i = 0; i < this.MaximumRedirections; i++) {
+ this.EnsureAllowableRequestUri(request.RequestUri, requireSsl);
+ DirectWebResponse response = this.chainedWebRequestHandler.GetResponse(request);
+ response.CacheNetworkStreamAndClose(this.MaximumBytesToRead);
+ if (response.Status == HttpStatusCode.MovedPermanently ||
+ response.Status == HttpStatusCode.Redirect ||
+ response.Status == HttpStatusCode.RedirectMethod ||
+ response.Status == HttpStatusCode.RedirectKeepVerb) {
+ if (request.Method == "POST") {
+ // We have no copy of the post entity stream to repeat on our manually
+ // cloned HttpWebRequest, so we have to bail.
+ ErrorUtilities.ThrowProtocol(MessagingStrings.UntrustedRedirectsOnPOSTNotSupported);
+ }
+ Uri redirectUri = new Uri(response.FinalUri, response.Headers[HttpResponseHeader.Location]);
+ request = request.Clone(redirectUri);
+ } else {
+ return response;
+ }
+ }
+
+ throw ErrorUtilities.ThrowProtocol(MessagingStrings.TooManyRedirects, originalRequestUri);
}
#endregion
@@ -227,91 +275,30 @@ namespace DotNetOpenAuth.Messaging {
#endregion
- internal DirectWebResponse RequestWithManagedRedirects(HttpWebRequest request, bool requireSsl) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
-
- // Since we may require SSL for every redirect, we handle each redirect manually
- // in order to detect and fail if any redirect sends us to an HTTP url.
- // We COULD allow automatic redirect in the cases where HTTPS is not required,
- // but our mock request infrastructure can't do redirects on its own either.
- Uri originalRequestUri = request.RequestUri;
- int i;
- for (i = 0; i < this.MaximumRedirections; i++) {
- DirectWebResponse response = this.RequestCore(request, null, originalRequestUri, requireSsl);
- if (response.Status == HttpStatusCode.MovedPermanently ||
- response.Status == HttpStatusCode.Redirect ||
- response.Status == HttpStatusCode.RedirectMethod ||
- response.Status == HttpStatusCode.RedirectKeepVerb) {
- Uri redirectUri = new Uri(response.FinalUri, response.Headers[HttpResponseHeader.Location]);
- request = CloneRequestWithNewUrl(request, redirectUri);
- } else {
- return response;
- }
- }
- throw new WebException(string.Format(CultureInfo.CurrentCulture, MessagingStrings.TooManyRedirects, originalRequestUri));
- }
-
- private static HttpWebRequest CloneRequestWithNewUrl(HttpWebRequest request, Uri newRequestUri) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNotNull(newRequestUri, "newRequestUri");
-
- var newRequest = (HttpWebRequest)WebRequest.Create(newRequestUri);
- newRequest.Accept = request.Accept;
- newRequest.AllowAutoRedirect = request.AllowAutoRedirect;
- newRequest.AllowWriteStreamBuffering = request.AllowWriteStreamBuffering;
- newRequest.AuthenticationLevel = request.AuthenticationLevel;
- newRequest.AutomaticDecompression = request.AutomaticDecompression;
- newRequest.CachePolicy = request.CachePolicy;
- newRequest.ClientCertificates = request.ClientCertificates;
- newRequest.ConnectionGroupName = request.ConnectionGroupName;
- if (request.ContentLength >= 0) {
- newRequest.ContentLength = request.ContentLength;
- }
- newRequest.ContentType = request.ContentType;
- newRequest.ContinueDelegate = request.ContinueDelegate;
- newRequest.CookieContainer = request.CookieContainer;
- newRequest.Credentials = request.Credentials;
- newRequest.Expect = request.Expect;
- newRequest.IfModifiedSince = request.IfModifiedSince;
- newRequest.ImpersonationLevel = request.ImpersonationLevel;
- newRequest.KeepAlive = request.KeepAlive;
- newRequest.MaximumAutomaticRedirections = request.MaximumAutomaticRedirections;
- newRequest.MaximumResponseHeadersLength = request.MaximumResponseHeadersLength;
- newRequest.MediaType = request.MediaType;
- newRequest.Method = request.Method;
- newRequest.Pipelined = request.Pipelined;
- newRequest.PreAuthenticate = request.PreAuthenticate;
- newRequest.ProtocolVersion = request.ProtocolVersion;
- newRequest.Proxy = request.Proxy;
- newRequest.ReadWriteTimeout = request.ReadWriteTimeout;
- newRequest.Referer = request.Referer;
- newRequest.SendChunked = request.SendChunked;
- newRequest.Timeout = request.Timeout;
- newRequest.TransferEncoding = request.TransferEncoding;
- newRequest.UnsafeAuthenticatedConnectionSharing = request.UnsafeAuthenticatedConnectionSharing;
- newRequest.UseDefaultCredentials = request.UseDefaultCredentials;
- newRequest.UserAgent = request.UserAgent;
-
- // We copy headers last, and only those that do not yet exist as a result
- // of setting these properties, so as to avoid exceptions thrown because
- // there are properties .NET wants us to use rather than direct headers.
- foreach (string header in request.Headers) {
- if (string.IsNullOrEmpty(newRequest.Headers[header])) {
- newRequest.Headers.Add(header, request.Headers[header]);
- }
- }
-
- return newRequest;
- }
-
private bool IsHostWhitelisted(string host) {
return this.IsHostInList(host, this.WhitelistHosts, this.WhitelistHostsRegex);
}
+ /// <summary>
+ /// Determines whether a given host is blacklisted.
+ /// </summary>
+ /// <param name="host">The host name to test.</param>
+ /// <returns>
+ /// <c>true</c> if the host is blacklisted; otherwise, <c>false</c>.
+ /// </returns>
private bool IsHostBlacklisted(string host) {
return this.IsHostInList(host, this.BlacklistHosts, this.BlacklistHostsRegex);
}
+ /// <summary>
+ /// Determines whether the given host name is in a host list or host name regex list.
+ /// </summary>
+ /// <param name="host">The host name.</param>
+ /// <param name="stringList">The list of host names.</param>
+ /// <param name="regexList">The list of regex patterns of host names.</param>
+ /// <returns>
+ /// <c>true</c> if the specified host falls within at least one of the given lists; otherwise, <c>false</c>.
+ /// </returns>
private bool IsHostInList(string host, ICollection<string> stringList, ICollection<Regex> regexList) {
ErrorUtilities.VerifyNonZeroLength(host, "host");
ErrorUtilities.VerifyArgumentNotNull(stringList, "stringList");
@@ -336,10 +323,17 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="requireSsl">If set to <c>true</c>, only web requests that can be made entirely over SSL will succeed.</param>
private void EnsureAllowableRequestUri(Uri requestUri, bool requireSsl) {
ErrorUtilities.VerifyArgument(this.IsUriAllowable(requestUri), MessagingStrings.UnsafeWebRequestDetected, requestUri);
-
ErrorUtilities.VerifyProtocol(!requireSsl || String.Equals(requestUri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase), MessagingStrings.InsecureWebRequestWithSslRequired, requestUri);
}
+ /// <summary>
+ /// Determines whether a URI is allowed based on scheme and host name.
+ /// No requireSSL check is done here
+ /// </summary>
+ /// <param name="uri">The URI.</param>
+ /// <returns>
+ /// <c>true</c> if [is URI allowable] [the specified URI]; otherwise, <c>false</c>.
+ /// </returns>
private bool IsUriAllowable(Uri uri) {
ErrorUtilities.VerifyArgumentNotNull(uri, "uri");
if (!this.allowableSchemes.Contains(uri.Scheme)) {
@@ -422,50 +416,5 @@ namespace DotNetOpenAuth.Messaging {
return request;
}
-
- private DirectWebResponse RequestCore(HttpWebRequest request, Stream postEntity, Uri originalRequestUri, bool requireSsl) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNotNull(originalRequestUri, "originalRequestUri");
- this.EnsureAllowableRequestUri(request.RequestUri, requireSsl);
-
- int postEntityLength = 0;
- try {
- if (postEntity != null) {
- using (Stream outStream = request.GetRequestStream()) {
- postEntityLength = postEntity.CopyTo(outStream);
- }
- }
-
- DirectWebResponse response = this.chainedWebRequestHandler.GetResponse(request);
- response.CacheNetworkStreamAndClose(this.MaximumBytesToRead);
- return response;
- } catch (WebException e) {
- using (HttpWebResponse response = (HttpWebResponse)e.Response) {
- if (response != null) {
- if (response.StatusCode == HttpStatusCode.ExpectationFailed) {
- if (request.ServicePoint.Expect100Continue) { // must only try this once more
- // Some OpenID servers doesn't understand the Expect header and send 417 error back.
- // If this server just failed from that, we're trying again without sending the
- // "Expect: 100-Continue" HTTP header. (see Google Code Issue 72)
- // We don't just set Expect100Continue = !avoidSendingExpect100Continue
- // so that future requests don't reset this and have to try twice as well.
- // We don't want to blindly set all ServicePoints to not use the Expect header
- // as that would be a security hole allowing any visitor to a web site change
- // the web site's global behavior when calling that host.
- request.ServicePoint.Expect100Continue = false; // TODO: investigate that CAS may throw here, and we can use request.Expect instead.
- postEntity.Seek(-postEntityLength, SeekOrigin.Current);
- request = CloneRequestWithNewUrl(request, request.RequestUri);
- return this.RequestCore(request, postEntity, originalRequestUri, requireSsl);
- }
- }
- var directResponse = new DirectWebResponse(originalRequestUri, response);
- directResponse.CacheNetworkStreamAndClose(this.MaximumBytesToRead);
- return directResponse;
- } else {
- throw ErrorUtilities.Wrap(e, MessagingStrings.WebRequestFailed, originalRequestUri);
- }
- }
- }
- }
}
}