/// <summary> /// 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> /// <param name="options">The options to apply to this web request.</param> /// <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 Stream GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options) { ErrorUtilities.VerifyArgumentNotNull(request, "request"); ErrorUtilities.VerifySupported(this.CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name); return(GetRequestStreamCore(request, options)); }
/// <summary> /// Adds message types to the set that this factory can create. /// </summary> /// <param name="messageTypes">The message types that this factory may instantiate.</param> public virtual void AddMessageTypes(IEnumerable <MessageDescription> messageTypes) { Requires.NotNull(messageTypes, "messageTypes"); Requires.True(messageTypes.All(msg => msg != null), "messageTypes"); var unsupportedMessageTypes = new List <MessageDescription>(0); foreach (MessageDescription messageDescription in messageTypes) { bool supportedMessageType = false; // First see whether this message fits the recognized pattern for request messages. if (typeof(IDirectedProtocolMessage).IsAssignableFrom(messageDescription.MessageType)) { foreach (ConstructorInfo ctor in messageDescription.Constructors) { ParameterInfo[] parameters = ctor.GetParameters(); if (parameters.Length == 2 && parameters[0].ParameterType == typeof(Uri) && parameters[1].ParameterType == typeof(Version)) { supportedMessageType = true; this.requestMessageTypes.Add(messageDescription, ctor); break; } } } // Also see if this message fits the recognized pattern for response messages. if (typeof(IDirectResponseProtocolMessage).IsAssignableFrom(messageDescription.MessageType)) { var responseCtors = new Dictionary <Type, ConstructorInfo>(messageDescription.Constructors.Length); foreach (ConstructorInfo ctor in messageDescription.Constructors) { ParameterInfo[] parameters = ctor.GetParameters(); if (parameters.Length == 1 && typeof(IDirectedProtocolMessage).IsAssignableFrom(parameters[0].ParameterType)) { responseCtors.Add(parameters[0].ParameterType, ctor); } } if (responseCtors.Count > 0) { supportedMessageType = true; this.responseMessageTypes.Add(messageDescription, responseCtors); } } if (!supportedMessageType) { unsupportedMessageTypes.Add(messageDescription); } } ErrorUtilities.VerifySupported( !unsupportedMessageTypes.Any(), MessagingStrings.StandardMessageFactoryUnsupportedMessageType, unsupportedMessageTypes.ToStringDeferred()); }
/// <summary> /// Gets the public facing URL for the given incoming HTTP request. /// </summary> /// <param name="request">The request.</param> /// <param name="serverVariables">The server variables to consider part of the request.</param> /// <returns> /// The URI that the outside world used to create this request. /// </returns> /// <remarks> /// Although the <paramref name="serverVariables"/> value can be obtained from /// <see cref="HttpRequest.ServerVariables"/>, it's useful to be able to pass them /// in so we can simulate injected values from our unit tests since the actual property /// is a read-only kind of <see cref="NameValueCollection"/>. /// </remarks> internal static Uri GetPublicFacingUrl(HttpRequest request, NameValueCollection serverVariables) { Contract.Requires <ArgumentNullException>(request != null); Contract.Requires <ArgumentNullException>(serverVariables != null); // Due to URL rewriting, cloud computing (i.e. Azure) // and web farms, etc., we have to be VERY careful about what // we consider the incoming URL. We want to see the URL as it would // appear on the public-facing side of the hosting web site. // HttpRequest.Url gives us the internal URL in a cloud environment, // So we use a variable that (at least from what I can tell) gives us // the public URL: if (serverVariables["HTTP_HOST"] != null) { ErrorUtilities.VerifySupported(request.Url.Scheme == Uri.UriSchemeHttps || request.Url.Scheme == Uri.UriSchemeHttp, "Only HTTP and HTTPS are supported protocols."); string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ?? request.Url.Scheme; Uri hostAndPort = new Uri(scheme + Uri.SchemeDelimiter + serverVariables["HTTP_HOST"]); UriBuilder publicRequestUri = new UriBuilder(request.Url); publicRequestUri.Scheme = scheme; publicRequestUri.Host = hostAndPort.Host; publicRequestUri.Port = hostAndPort.Port; // CC missing Uri.Port contract that's on UriBuilder.Port return(publicRequestUri.Uri); } else { // Failover to the method that works for non-web farm enviroments. // 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(request.Url, request.RawUrl)); } }
/// <summary> /// 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> /// <param name="options">The options to apply to this web request.</param> /// <returns> /// An instance of <see cref="DirectWebResponse"/> describing the response. /// </returns> /// <exception cref="ProtocolException">Thrown for any network error.</exception> /// <remarks> /// <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. The <see cref="WebException.Response"/> /// value, if set, shoud be Closed before throwing.</para> /// </remarks> public DirectWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) { ErrorUtilities.VerifyArgumentNotNull(request, "request"); ErrorUtilities.VerifySupported(this.CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name); // This request MAY have already been prepared by GetRequestStream, but // we have no guarantee, so do it just to be safe. PrepareRequest(request, false); try { Logger.DebugFormat("HTTP {0} {1}", request.Method, request.RequestUri); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); return(new NetworkDirectWebResponse(request.RequestUri, response)); } catch (WebException ex) { 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, alter the ServicePoint for this server // so that we don't send that header again next time (whenever that is). // "Expect: 100-Continue" HTTP header. (see Google Code Issue 72) // 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 // An alternative to ServicePoint if we don't have permission to set that, // but we'd have to set it BEFORE each request. ////request.Expect = ""; } if ((options & DirectWebRequestOptions.AcceptAllHttpResponses) != 0 && response != null && response.StatusCode != HttpStatusCode.ExpectationFailed) { Logger.InfoFormat("The HTTP error code {0} {1} is being accepted because the {2} flag is set.", (int)response.StatusCode, response.StatusCode, DirectWebRequestOptions.AcceptAllHttpResponses); return(new NetworkDirectWebResponse(request.RequestUri, response)); } if (Logger.IsErrorEnabled) { if (response != null) { using (var reader = new StreamReader(ex.Response.GetResponseStream())) { Logger.ErrorFormat("WebException from {0}: {1}{2}", ex.Response.ResponseUri, Environment.NewLine, reader.ReadToEnd()); } } else { Logger.ErrorFormat("WebException {1} from {0}, no response available.", request.RequestUri, ex.Status); } } // Be sure to close the response stream to conserve resources and avoid // filling up all our incoming pipes and denying future requests. // If in the future, some callers actually want to read this response // we'll need to figure out how to reliably call Close on exception // responses at all callers. response.Close(); throw ErrorUtilities.Wrap(ex, MessagingStrings.ErrorInRequestReplyMessage); } }