public void Uri_conversion_of_segments_containing_colon_with_double_encoding_turned_off_and_back_on() { XUri xuri = new XUri("http://server/foo:baz/bar").WithoutSegmentDoubleEncoding().WithSegmentDoubleEncoding(); Uri uri = xuri.ToUri(); Assert.AreEqual("http://server/foo%253Abaz/bar", uri.ToString()); }
public void TestUriConversion() { XUri xuri = new XUri("http://*****:*****@server/foo/bar?query=param#fragment"); Uri uri = xuri.ToUri(); Assert.AreEqual("http://*****:*****@server/foo/bar?query=param#fragment", uri.ToString()); }
public void TestUriConversionForSegmentsEndingInDots() { XUri xuri = new XUri("http://server/foo.../bar"); Uri uri = xuri.ToUri(); Assert.AreEqual("http://server/foo%252E%252E%252E/bar", uri.ToString()); }
public void Uri_conversion_of_segments_containing_colon() { XUri xuri = new XUri("http://server/foo:baz/bar"); Uri uri = xuri.ToUri(); Assert.AreEqual("http://server/foo%253Abaz/bar", uri.ToString()); }
private Yield HandleInvoke(Action<string> activity, Plug plug, string verb, XUri uri, DreamMessage request, Result<DreamMessage> response) { Result<IAsyncResult> async; // remove internal headers request.Headers.DreamTransport = null; // set request headers request.Headers.Host = uri.Host; if(request.Headers.UserAgent == null) { request.Headers.UserAgent = "Dream/" + DreamUtil.DreamVersion; } // add cookies to request if(request.HasCookies) { request.Headers[DreamHeaders.COOKIE] = DreamCookie.RenderCookieHeader(request.Cookies); } // initialize request activity("pre WebRequest.Create"); var httpRequest = (HttpWebRequest)WebRequest.Create(uri.ToUri()); activity("post WebRequest.Create"); httpRequest.Method = verb; httpRequest.Timeout = System.Threading.Timeout.Infinite; httpRequest.ReadWriteTimeout = System.Threading.Timeout.Infinite; // Note (arnec): httpRequest AutoRedirect is disabled because Plug is responsible for it (this allows redirects to follow // the appropriate handler instead staying stuck in http end point land httpRequest.AllowAutoRedirect = false; // Note from http://support.microsoft.com/kb/904262 // The HTTP request is made up of the following parts: // 1. Sending the request is covered by using the HttpWebRequest.Timeout method. // 2. Getting the response header is covered by using the HttpWebRequest.Timeout method. // 3. Reading the body of the response is not covered by using the HttpWebResponse.Timeout method. In ASP.NET 1.1 and in later versions, reading the body of the response // is covered by using the HttpWebRequest.ReadWriteTimeout method. The HttpWebRequest.ReadWriteTimeout method is used to handle cases where the response headers are // retrieved in a timely manner but where the reading of the response body times out. httpRequest.KeepAlive = false; httpRequest.ProtocolVersion = System.Net.HttpVersion.Version10; // TODO (steveb): set default proxy //httpRequest.Proxy = WebProxy.GetDefaultProxy(); //httpRequest.Proxy.Credentials = CredentialCache.DefaultCredentials; // set credentials if(plug.Credentials != null) { httpRequest.Credentials = plug.Credentials; httpRequest.PreAuthenticate = true; } else if(!string.IsNullOrEmpty(uri.User) || !string.IsNullOrEmpty(uri.Password)) { httpRequest.Credentials = new NetworkCredential(uri.User ?? string.Empty, uri.Password ?? string.Empty); httpRequest.PreAuthenticate = true; // Note (arnec): this manually adds the basic auth header, so it can authorize // in a single request without requiring challenge var authbytes = Encoding.ASCII.GetBytes(string.Concat(uri.User ?? string.Empty, ":", uri.Password ?? string.Empty)); var base64 = Convert.ToBase64String(authbytes); httpRequest.Headers.Add(DreamHeaders.AUTHORIZATION, "Basic " + base64); } // add request headres foreach(KeyValuePair<string, string> header in request.Headers) { HttpUtil.AddHeader(httpRequest, header.Key, header.Value); } // send message stream if((request.ContentLength != 0) || (verb == Verb.POST)) { async = new Result<IAsyncResult>(); try { activity("pre BeginGetRequestStream"); httpRequest.BeginGetRequestStream(async.Return, null); activity("post BeginGetRequestStream"); } catch(Exception e) { activity("pre HandleResponse 1"); if(!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetRequestStream"); yield return async.Catch(); activity("post yield BeginGetRequestStream"); // send request Stream outStream; try { activity("pre EndGetRequestStream"); outStream = httpRequest.EndGetRequestStream(async.Value); activity("pre EndGetRequestStream"); } catch(Exception e) { activity("pre HandleResponse 2"); if(!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } // copy data using(outStream) { Result<long> res; activity("pre yield CopyStream"); yield return res = request.ToStream().CopyTo(outStream, request.ContentLength, new Result<long>(TimeSpan.MaxValue)).Catch(); activity("post yield CopyStream"); if(res.HasException) { activity("pre HandleResponse 3"); if(!HandleResponse(activity, res.Exception, null, response)) { _log.ErrorExceptionMethodCall(res.Exception, "*****@*****.**", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } } request = null; // wait for response async = new Result<IAsyncResult>(response.Timeout); try { activity("pre BeginGetResponse"); httpRequest.BeginGetResponse(async.Return, null); activity("post BeginGetResponse"); } catch(Exception e) { activity("pre HandleResponse 4"); if(!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetResponse"); yield return async.Catch(); activity("post yield BeginGetResponse"); // check if an error occurred if(async.HasException) { activity("pre HandleResponse 5"); if(!HandleResponse(activity, async.Exception, null, response)) { _log.ErrorExceptionMethodCall(async.Exception, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } else { // handle response try { HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(async.Value); activity("pre HandleResponse 6"); if(!HandleResponse(activity, null, httpResponse, response)) { try { httpRequest.Abort(); } catch { } } } catch(Exception e) { activity("pre HandleResponse 7"); if(!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } }
public Yield UserLogin(DreamContext context, DreamMessage request, Result <DreamMessage> response) { string userSuppliedIdentifier = context.GetParam("url", null); if (String.IsNullOrEmpty(userSuppliedIdentifier)) { _log.Info("No identifier was specified"); throw new DreamBadRequestException("No identifier was specified."); } XUri returnUri = new XUri(context.GetParam("returnurl", null)); String realm = context.GetParam("realm", null); if (String.IsNullOrEmpty(realm)) { realm = returnUri.WithoutPathQueryFragment().ToString(); } IAuthenticationRequest openIdRequest; // dummy parameters required by DotNetOpenId 2.x; in 3.x, you can // just pass null to the OpenIdRelyingParty constructor. Uri identifierUri = new Uri(userSuppliedIdentifier); NameValueCollection queryCol = System.Web.HttpUtility.ParseQueryString(identifierUri.Query); OpenIdRelyingParty openid = new OpenIdRelyingParty(null, identifierUri, queryCol); // creating an OpenID request will authenticate that // the endpoint exists and is an OpenID provider. _log.DebugFormat("Creating OpenID request: identifier {0}, return URL {1}, realm {2}", userSuppliedIdentifier, returnUri.ToString(), realm); try { openIdRequest = openid.CreateRequest( userSuppliedIdentifier, realm, returnUri.ToUri()); } catch (OpenIdException ex) { _log.WarnFormat("'{0}' rejected as OpenID identifier: {1}", userSuppliedIdentifier, ex.Message); throw new DreamBadRequestException(string.Format("'{0}' is not a valid OpenID identifier. {1}", userSuppliedIdentifier, ex.Message)); } // Ask for the e-mail address on this request. // Use both SREG and AX, to increase the odds of getting it. openIdRequest.AddExtension(new ClaimsRequest { Email = DemandLevel.Require, }); var fetch = new FetchRequest(); fetch.AddAttribute(new AttributeRequest(WellKnownAttributes.Contact.Email, true)); openIdRequest.AddExtension(fetch); // The RedirectingResponse either contains a "Location" header for // a HTTP GET, which will return in the response as 'endpoint', or // a HTML FORM which needs to be displayed to the user, which will // return in the response as 'form'. IResponse wr = openIdRequest.RedirectingResponse; XDoc result = new XDoc("openid"); if (String.IsNullOrEmpty(wr.Headers["Location"])) { System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding(); string formBody = enc.GetString(wr.Body); _log.DebugFormat("OpenID redirect by HTML FORM: {0}", formBody); result.Attr("form", formBody); } else { string redirectUrl = wr.Headers["Location"]; _log.DebugFormat("OpenID redirect URL: {0}", redirectUrl); result.Attr("endpoint", redirectUrl); } response.Return(DreamMessage.Ok(result)); yield break; }
private Yield HandleInvoke(Action <string> activity, Plug plug, string verb, XUri uri, DreamMessage request, Result <DreamMessage> response) { Result <IAsyncResult> async; // remove internal headers request.Headers.DreamTransport = null; // set request headers request.Headers.Host = uri.Host; if (request.Headers.UserAgent == null) { request.Headers.UserAgent = "Dream/" + DreamUtil.DreamVersion; } // add cookies to request if (request.HasCookies) { request.Headers[DreamHeaders.COOKIE] = DreamCookie.RenderCookieHeader(request.Cookies); } // initialize request activity("pre WebRequest.Create"); var httpRequest = (HttpWebRequest)WebRequest.Create(uri.ToUri()); activity("post WebRequest.Create"); httpRequest.Method = verb; httpRequest.Timeout = System.Threading.Timeout.Infinite; httpRequest.ReadWriteTimeout = System.Threading.Timeout.Infinite; // Note (arnec): httpRequest AutoRedirect is disabled because Plug is responsible for it (this allows redirects to follow // the appropriate handler instead staying stuck in http end point land httpRequest.AllowAutoRedirect = false; // Note from http://support.microsoft.com/kb/904262 // The HTTP request is made up of the following parts: // 1. Sending the request is covered by using the HttpWebRequest.Timeout method. // 2. Getting the response header is covered by using the HttpWebRequest.Timeout method. // 3. Reading the body of the response is not covered by using the HttpWebResponse.Timeout method. In ASP.NET 1.1 and in later versions, reading the body of the response // is covered by using the HttpWebRequest.ReadWriteTimeout method. The HttpWebRequest.ReadWriteTimeout method is used to handle cases where the response headers are // retrieved in a timely manner but where the reading of the response body times out. httpRequest.KeepAlive = false; httpRequest.ProtocolVersion = System.Net.HttpVersion.Version10; // TODO (steveb): set default proxy //httpRequest.Proxy = WebProxy.GetDefaultProxy(); //httpRequest.Proxy.Credentials = CredentialCache.DefaultCredentials; // set credentials if (plug.Credentials != null) { httpRequest.Credentials = plug.Credentials; httpRequest.PreAuthenticate = true; } else if (!string.IsNullOrEmpty(uri.User) || !string.IsNullOrEmpty(uri.Password)) { httpRequest.Credentials = new NetworkCredential(uri.User ?? string.Empty, uri.Password ?? string.Empty); httpRequest.PreAuthenticate = true; // Note (arnec): this manually adds the basic auth header, so it can authorize // in a single request without requiring challenge var authbytes = Encoding.ASCII.GetBytes(string.Concat(uri.User ?? string.Empty, ":", uri.Password ?? string.Empty)); var base64 = Convert.ToBase64String(authbytes); httpRequest.Headers.Add(DreamHeaders.AUTHORIZATION, "Basic " + base64); } // add request headres foreach (KeyValuePair <string, string> header in request.Headers) { HttpUtil.AddHeader(httpRequest, header.Key, header.Value); } // send message stream if ((request.ContentLength != 0) || (verb == Verb.POST)) { async = new Result <IAsyncResult>(); try { activity("pre BeginGetRequestStream"); httpRequest.BeginGetRequestStream(async.Return, null); activity("post BeginGetRequestStream"); } catch (Exception e) { activity("pre HandleResponse 1"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetRequestStream"); yield return(async.Catch()); activity("post yield BeginGetRequestStream"); // send request Stream outStream; try { activity("pre EndGetRequestStream"); outStream = httpRequest.EndGetRequestStream(async.Value); activity("pre EndGetRequestStream"); } catch (Exception e) { activity("pre HandleResponse 2"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } // copy data using (outStream) { Result <long> res; activity("pre yield CopyStream"); yield return(res = request.ToStream().CopyToStream(outStream, request.ContentLength, new Result <long>(TimeSpan.MaxValue)).Catch()); activity("post yield CopyStream"); if (res.HasException) { activity("pre HandleResponse 3"); if (!HandleResponse(activity, res.Exception, null, response)) { _log.ErrorExceptionMethodCall(res.Exception, "*****@*****.**", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } } request = null; // wait for response async = new Result <IAsyncResult>(response.Timeout); try { activity("pre BeginGetResponse"); httpRequest.BeginGetResponse(async.Return, null); activity("post BeginGetResponse"); } catch (Exception e) { activity("pre HandleResponse 4"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetResponse"); yield return(async.Catch()); activity("post yield BeginGetResponse"); // check if an error occurred if (async.HasException) { activity("pre HandleResponse 5"); if (!HandleResponse(activity, async.Exception, null, response)) { _log.ErrorExceptionMethodCall(async.Exception, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } else { // handle response try { HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(async.Value); activity("pre HandleResponse 6"); if (!HandleResponse(activity, null, httpResponse, response)) { try { httpRequest.Abort(); } catch { } } } catch (Exception e) { activity("pre HandleResponse 7"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } }
private Yield HandleInvoke(Action <string> activity, Plug plug, string verb, XUri uri, DreamMessage request, Result <DreamMessage> response) { Result <IAsyncResult> async; // remove internal headers request.Headers.DreamTransport = null; // set request headers request.Headers.Host = uri.Host; if (request.Headers.UserAgent == null) { request.Headers.UserAgent = "Dream/" + DreamUtil.DreamVersion; } // add cookies to request if (request.HasCookies) { request.Headers[DreamHeaders.COOKIE] = DreamCookie.RenderCookieHeader(request.Cookies); } // check if we can pool the request with an existing one if ((plug.Credentials == null) && StringUtil.ContainsInvariantIgnoreCase(verb, "GET")) { // create the request hashcode StringBuilder buffer = new StringBuilder(); buffer.AppendLine(uri.ToString()); foreach (KeyValuePair <string, string> header in request.Headers) { buffer.Append(header.Key).Append(": ").Append(header.Value).AppendLine(); } Guid hash = new Guid(StringUtil.ComputeHash(buffer.ToString())); // check if an active connection exists Result <DreamMessage> relay = null; lock (_requests) { List <Result <DreamMessage> > pending; if (_requests.TryGetValue(hash, out pending)) { relay = new Result <DreamMessage>(response.Timeout); pending.Add(relay); } else { pending = new List <Result <DreamMessage> >(); pending.Add(response); _requests[hash] = pending; } } // check if we're pooling a request if (relay != null) { // wait for the relayed response yield return(relay); response.Return(relay); yield break; } else { // NOTE (steveb): we use TaskEnv.Instantaneous so that we don't exit the current stack frame before we've executed the continuation; // otherwise, we'll trigger an exception because our result object may not be set. // create new handler to multicast the response to the relays response = new Result <DreamMessage>(response.Timeout, TaskEnv.Instantaneous); response.WhenDone(_ => { List <Result <DreamMessage> > pending; lock (_requests) { _requests.TryGetValue(hash, out pending); _requests.Remove(hash); } // this check should never fail! if (response.HasException) { // send the exception to all relays foreach (Result <DreamMessage> result in pending) { result.Throw(response.Exception); } } else { DreamMessage original = response.Value; // only memorize the message if it needs to be cloned if (pending.Count > 1) { // clone the message to all relays original.Memorize(new Result()).Wait(); foreach (var result in pending) { result.Return(original.Clone()); } } else { // relay the original message pending[0].Return(original); } } }); } } // initialize request activity("pre WebRequest.Create"); var httpRequest = (HttpWebRequest)WebRequest.Create(uri.ToUri()); activity("post WebRequest.Create"); httpRequest.Method = verb; httpRequest.Timeout = System.Threading.Timeout.Infinite; httpRequest.ReadWriteTimeout = System.Threading.Timeout.Infinite; // Note (arnec): httpRequest AutoRedirect is disabled because Plug is responsible for it (this allows redirects to follow // the appropriate handler instead staying stuck in http end point land httpRequest.AllowAutoRedirect = false; // Note from http://support.microsoft.com/kb/904262 // The HTTP request is made up of the following parts: // 1. Sending the request is covered by using the HttpWebRequest.Timeout method. // 2. Getting the response header is covered by using the HttpWebRequest.Timeout method. // 3. Reading the body of the response is not covered by using the HttpWebResponse.Timeout method. In ASP.NET 1.1 and in later versions, reading the body of the response // is covered by using the HttpWebRequest.ReadWriteTimeout method. The HttpWebRequest.ReadWriteTimeout method is used to handle cases where the response headers are // retrieved in a timely manner but where the reading of the response body times out. httpRequest.KeepAlive = false; httpRequest.ProtocolVersion = System.Net.HttpVersion.Version10; // TODO (steveb): set default proxy //httpRequest.Proxy = WebProxy.GetDefaultProxy(); //httpRequest.Proxy.Credentials = CredentialCache.DefaultCredentials; // set credentials if (plug.Credentials != null) { httpRequest.Credentials = plug.Credentials; httpRequest.PreAuthenticate = true; } else if (!string.IsNullOrEmpty(uri.User) || !string.IsNullOrEmpty(uri.Password)) { httpRequest.Credentials = new NetworkCredential(uri.User ?? string.Empty, uri.Password ?? string.Empty); httpRequest.PreAuthenticate = true; // Note (arnec): this manually adds the basic auth header, so it can authorize // in a single request without requiring challenge var authbytes = Encoding.ASCII.GetBytes(string.Concat(uri.User ?? string.Empty, ":", uri.Password ?? string.Empty)); var base64 = Convert.ToBase64String(authbytes); httpRequest.Headers.Add(DreamHeaders.AUTHORIZATION, "Basic " + base64); } // add request headres foreach (KeyValuePair <string, string> header in request.Headers) { HttpUtil.AddHeader(httpRequest, header.Key, header.Value); } // send message stream if ((request.ContentLength != 0) || (verb == Verb.POST)) { async = new Result <IAsyncResult>(); try { activity("pre BeginGetRequestStream"); httpRequest.BeginGetRequestStream(async.Return, null); activity("post BeginGetRequestStream"); } catch (Exception e) { activity("pre HandleResponse 1"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetRequestStream"); yield return(async.Catch()); activity("post yield BeginGetRequestStream"); // send request Stream outStream; try { activity("pre EndGetRequestStream"); outStream = httpRequest.EndGetRequestStream(async.Value); activity("pre EndGetRequestStream"); } catch (Exception e) { activity("pre HandleResponse 2"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } // copy data using (outStream) { Result <long> res; activity("pre yield CopyStream"); yield return(res = request.ToStream().CopyTo(outStream, request.ContentLength, new Result <long>(TimeSpan.MaxValue)).Catch()); activity("post yield CopyStream"); if (res.HasException) { activity("pre HandleResponse 3"); if (!HandleResponse(activity, res.Exception, null, response)) { _log.ErrorExceptionMethodCall(res.Exception, "*****@*****.**", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } } request = null; // wait for response async = new Result <IAsyncResult>(response.Timeout); try { activity("pre BeginGetResponse"); httpRequest.BeginGetResponse(async.Return, null); activity("post BeginGetResponse"); } catch (Exception e) { activity("pre HandleResponse 4"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetResponse"); yield return(async.Catch()); activity("post yield BeginGetResponse"); // check if an error occurred if (async.HasException) { activity("pre HandleResponse 5"); if (!HandleResponse(activity, async.Exception, null, response)) { _log.ErrorExceptionMethodCall(async.Exception, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } else { // handle response try { HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(async.Value); activity("pre HandleResponse 6"); if (!HandleResponse(activity, null, httpResponse, response)) { try { httpRequest.Abort(); } catch { } } } catch (Exception e) { activity("pre HandleResponse 7"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } }
private Yield HandleInvoke(Action<string> activity, Plug plug, string verb, XUri uri, DreamMessage request, Result<DreamMessage> response) { Result<IAsyncResult> async; // remove internal headers request.Headers.DreamTransport = null; // set request headers request.Headers.Host = uri.Host; if(request.Headers.UserAgent == null) { request.Headers.UserAgent = "Dream/" + DreamUtil.DreamVersion; } // add cookies to request if(request.HasCookies) { request.Headers[DreamHeaders.COOKIE] = DreamCookie.RenderCookieHeader(request.Cookies); } // check if we can pool the request with an existing one if((plug.Credentials == null) && StringUtil.ContainsInvariantIgnoreCase(verb, "GET")) { // create the request hashcode StringBuilder buffer = new StringBuilder(); buffer.AppendLine(uri.ToString()); foreach(KeyValuePair<string, string> header in request.Headers) { buffer.Append(header.Key).Append(": ").Append(header.Value).AppendLine(); } Guid hash = new Guid(StringUtil.ComputeHash(buffer.ToString())); // check if an active connection exists Result<DreamMessage> relay = null; lock(_requests) { List<Result<DreamMessage>> pending; if(_requests.TryGetValue(hash, out pending)) { relay = new Result<DreamMessage>(response.Timeout); pending.Add(relay); } else { pending = new List<Result<DreamMessage>>(); pending.Add(response); _requests[hash] = pending; } } // check if we're pooling a request if(relay != null) { // wait for the relayed response yield return relay; response.Return(relay); yield break; } else { // NOTE (steveb): we use TaskEnv.Instantaneous so that we don't exit the current stack frame before we've executed the continuation; // otherwise, we'll trigger an exception because our result object may not be set. // create new handler to multicast the response to the relays response = new Result<DreamMessage>(response.Timeout, TaskEnv.Instantaneous); response.WhenDone(_ => { List<Result<DreamMessage>> pending; lock(_requests) { _requests.TryGetValue(hash, out pending); _requests.Remove(hash); } // this check should never fail! if(response.HasException) { // send the exception to all relays foreach(Result<DreamMessage> result in pending) { result.Throw(response.Exception); } } else { DreamMessage original = response.Value; // only memorize the message if it needs to be cloned if(pending.Count > 1) { // clone the message to all relays foreach(Result<DreamMessage> result in pending) { result.Return(original.Clone()); } } else { // relay the original message pending[0].Return(original); } } }); } } // initialize request activity("pre WebRequest.Create"); var httpRequest = (HttpWebRequest)WebRequest.Create(uri.ToUri()); activity("post WebRequest.Create"); httpRequest.Method = verb; httpRequest.Timeout = System.Threading.Timeout.Infinite; httpRequest.ReadWriteTimeout = System.Threading.Timeout.Infinite; // Note (arnec): httpRequest AutoRedirect is disabled because Plug is responsible for it (this allows redirects to follow // the appropriate handler instead staying stuck in http end point land httpRequest.AllowAutoRedirect = false; // Note from http://support.microsoft.com/kb/904262 // The HTTP request is made up of the following parts: // 1. Sending the request is covered by using the HttpWebRequest.Timeout method. // 2. Getting the response header is covered by using the HttpWebRequest.Timeout method. // 3. Reading the body of the response is not covered by using the HttpWebResponse.Timeout method. In ASP.NET 1.1 and in later versions, reading the body of the response // is covered by using the HttpWebRequest.ReadWriteTimeout method. The HttpWebRequest.ReadWriteTimeout method is used to handle cases where the response headers are // retrieved in a timely manner but where the reading of the response body times out. httpRequest.KeepAlive = false; httpRequest.ProtocolVersion = System.Net.HttpVersion.Version10; // TODO (steveb): set default proxy //httpRequest.Proxy = WebProxy.GetDefaultProxy(); //httpRequest.Proxy.Credentials = CredentialCache.DefaultCredentials; // set credentials if(plug.Credentials != null) { httpRequest.Credentials = plug.Credentials; httpRequest.PreAuthenticate = true; } else if(!string.IsNullOrEmpty(uri.User) || !string.IsNullOrEmpty(uri.Password)) { httpRequest.Credentials = new NetworkCredential(uri.User ?? string.Empty, uri.Password ?? string.Empty); httpRequest.PreAuthenticate = true; // Note (arnec): this manually adds the basic auth header, so it can authorize // in a single request without requiring challenge var authbytes = Encoding.ASCII.GetBytes(string.Concat(uri.User ?? string.Empty, ":", uri.Password ?? string.Empty)); var base64 = Convert.ToBase64String(authbytes); httpRequest.Headers.Add(DreamHeaders.AUTHORIZATION, "Basic " + base64); } // add request headres foreach(KeyValuePair<string, string> header in request.Headers) { HttpUtil.AddHeader(httpRequest, header.Key, header.Value); } // send message stream if((request.ContentLength != 0) || (verb == Verb.POST)) { async = new Result<IAsyncResult>(); try { activity("pre BeginGetRequestStream"); httpRequest.BeginGetRequestStream(async.Return, null); activity("post BeginGetRequestStream"); } catch(Exception e) { activity("pre HandleResponse 1"); if(!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetRequestStream"); yield return async.Catch(); activity("post yield BeginGetRequestStream"); // send request Stream outStream; try { activity("pre EndGetRequestStream"); outStream = httpRequest.EndGetRequestStream(async.Value); activity("pre EndGetRequestStream"); } catch(Exception e) { activity("pre HandleResponse 2"); if(!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } // copy data using(outStream) { Result<long> res; activity("pre yield CopyStream"); yield return res = request.ToStream().CopyTo(outStream, request.ContentLength, new Result<long>(TimeSpan.MaxValue)).Catch(); activity("post yield CopyStream"); if(res.HasException) { activity("pre HandleResponse 3"); if(!HandleResponse(activity, res.Exception, null, response)) { _log.ErrorExceptionMethodCall(res.Exception, "*****@*****.**", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } } request = null; // wait for response async = new Result<IAsyncResult>(response.Timeout); try { activity("pre BeginGetResponse"); httpRequest.BeginGetResponse(async.Return, null); activity("post BeginGetResponse"); } catch(Exception e) { activity("pre HandleResponse 4"); if(!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetResponse"); yield return async.Catch(); activity("post yield BeginGetResponse"); // check if an error occurred if(async.HasException) { activity("pre HandleResponse 5"); if(!HandleResponse(activity, async.Exception, null, response)) { _log.ErrorExceptionMethodCall(async.Exception, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } else { // handle response try { HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(async.Value); activity("pre HandleResponse 6"); if(!HandleResponse(activity, null, httpResponse, response)) { try { httpRequest.Abort(); } catch { } } } catch(Exception e) { activity("pre HandleResponse 7"); if(!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } }