Пример #1
0
        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());
        }
Пример #2
0
        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());
        }
Пример #3
0
        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());
        }
Пример #4
0
        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());
        }
Пример #5
0
        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;
                }
            }
        }
Пример #6
0
        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;
        }
Пример #7
0
 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());
 }
Пример #8
0
 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());
 }
Пример #9
0
        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());
        }
Пример #10
0
        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());
        }
Пример #11
0
        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;
                }
            }
        }
Пример #13
0
        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;
                }
            }
        }