Esempio n. 1
0
        public static Action WithEnv <T>(this Func <T> handler, TaskEnv env, Result <T> result)
        {
            if (handler == null)
            {
                throw new ArgumentNullException("handler");
            }
            System.Diagnostics.StackTrace stacktrace = DebugUtil.GetStackTrace();
            env.Acquire();
            return(() => {
                try {
                    T response = default(T);
                    Exception exception = env.InvokeNow(() => {
                        response = handler();
                    });
                    env.Release();

                    // check if a result object was provided
                    if (result != null)
                    {
                        if (exception != null)
                        {
                            result.Throw(exception);
                        }
                        else
                        {
                            result.Return(response);
                        }
                    }
                } catch (Exception e) {
                    _log.ErrorExceptionMethodCall(e, "Execution failed for state wrapped handler", stacktrace, handler.Method.Name);
                }
            });
        }
Esempio n. 2
0
        /// <summary>
        /// Invoke a zero arg action.
        /// </summary>
        /// <param name="handler">Action to invoke.</param>
        public void Invoke(Action handler)
        {
            if (handler == null)
            {
                throw new ArgumentNullException("handler");
            }
            if (_referenceCount < 1)
            {
                throw new InvalidOperationException("Cannot call invoke an unaquired TaskEnv");
            }
#pragma warning disable 219
            System.Diagnostics.StackTrace stacktrace = DebugUtil.GetStackTrace();
#pragma warning restore 219

            // check if handler can be invoked in-place or needs to queued up
            if (_dispatchQueue != null)
            {
                _dispatchQueue.QueueWorkItem(() => {
                    // store current thread-specific settings
                    TaskEnv previousEnv = _currentEnv;
                    try {
                        // set thread-specific settings
                        _currentEnv = this;

                        // execute handler
                        handler();
                    } catch (Exception e) {
                        _log.ErrorExceptionMethodCall(e, "Invoke: unhandled exception in handler");
                    } finally {
                        Release();

                        // restore thread-specific settings
                        _currentEnv = previousEnv;
                    }
                });
            }
            else
            {
                // store current thread-specific settings
                TaskEnv previousEnv = _currentEnv;
                try {
                    // set thread-specific settings
                    _currentEnv = this;

                    // execute handler
                    handler();
                } catch (Exception e) {
                    _log.WarnExceptionMethodCall(e, "Invoke: unhandled exception in handler");
                } finally {
                    Release();

                    // restore thread-specific settings
                    _currentEnv = previousEnv;
                }
            }
        }
Esempio n. 3
0
 internal static bool Shutdown(TimeSpan timeout)
 {
     // stop the thread timer
     _running = false;
     if ((timeout != TimeSpan.MaxValue && !_stopped.WaitOne((int)timeout.TotalMilliseconds, true)) || (timeout == TimeSpan.MaxValue && !_stopped.WaitOne())
         )
     {
         _log.ErrorExceptionMethodCall(new TimeoutException("GlobalClock thread shutdown timed out"), "Shutdown");
         return(false);
     }
     return(true);
 }
Esempio n. 4
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;
                }
            }
        }
Esempio n. 5
0
        private void RequestHandler(IAsyncResult ar)
        {
            HttpListenerContext httpContext = null;
            Action <string>     activity    = null;
            HttpListener        listener    = (HttpListener)ar.AsyncState;

            // try to finish getting the current context
            try {
                httpContext = listener.EndGetContext(ar);
            } catch (Exception e) {
                _log.WarnExceptionFormat(e, "unable to finish acquiring the request context, unable to handle request");
            }

            // start listening for next request
            if (!listener.IsListening)
            {
                _log.Debug("dropping out of request handler, since the listener is no longer listening");
                return;
            }
            try {
                listener.BeginGetContext(RequestHandler, listener);
            } catch (Exception e) {
                _log.WarnExceptionFormat(e, "unable to re-aquire context, dropping out of request handler");
                return;
            }

            // if we didn't succeed in ending the GetContext call, drop out
            if (httpContext == null)
            {
                return;
            }
            DreamMessage request = null;

            try {
                // finish listening for current context
                string[] prefixes = new string[listener.Prefixes.Count];
                listener.Prefixes.CopyTo(prefixes, 0);
                XUri requestUri = HttpUtil.FromHttpContext(httpContext);
                _log.DebugMethodCall("RequestHandler", httpContext.Request.HttpMethod, requestUri);

                // create request message
                request = new DreamMessage(DreamStatus.Ok, new DreamHeaders(httpContext.Request.Headers), MimeType.New(httpContext.Request.ContentType), httpContext.Request.ContentLength64, httpContext.Request.InputStream);
                DreamUtil.PrepareIncomingMessage(request, httpContext.Request.ContentEncoding, prefixes[0], httpContext.Request.RemoteEndPoint.ToString(), httpContext.Request.UserAgent);
                requestUri = requestUri.AuthorizeDreamInParams(request, _dreamInParamAuthtoken);

                // check if the request was forwarded through Apache mod_proxy
                string hostname = requestUri.GetParam(DreamInParam.HOST, null) ?? request.Headers.ForwardedHost ?? request.Headers.Host ?? requestUri.HostPort;
                activity = new ActivityState(_env, httpContext.Request.HttpMethod, httpContext.Request.Url.ToString(), hostname).Message;
                activity("RequestHandler");

                // process message
                _env.UpdateInfoMessage(_sourceExternal, null);
                string verb = httpContext.Request.HttpMethod;
                _env.SubmitRequestAsync(verb, requestUri, httpContext.User, request, new Result <DreamMessage>(TimeSpan.MaxValue))
                .WhenDone(result => Coroutine.Invoke(ResponseHandler, request, result, httpContext, activity, new Result(TimeSpan.MaxValue)));
            } catch (Exception ex) {
                _log.ErrorExceptionMethodCall(ex, "RequestHandler");
                if (request != null)
                {
                    request.Close();
                }
                try {
                    DreamMessage response = DreamMessage.InternalError(ex);
                    httpContext.Response.StatusCode = (int)response.Status;
                    Stream stream = response.ToStream();
                    httpContext.Response.Headers.Clear();
                    foreach (KeyValuePair <string, string> pair in response.Headers)
                    {
                        HttpUtil.AddHeader(httpContext.Response, pair.Key, pair.Value);
                    }
                    httpContext.Response.KeepAlive = false;
                    long size = response.ContentLength;
                    if (((size == -1) || (size > 0)) && (stream != Stream.Null))
                    {
                        CopyStream(delegate { }, stream, httpContext.Response.OutputStream, size, new Result <long>(DreamHostService.MAX_REQUEST_TIME)).Block();
                    }
                    httpContext.Response.OutputStream.Flush();
                } catch {
                    httpContext.Response.StatusCode = (int)DreamStatus.InternalError;
                }
                httpContext.Response.Close();
                if (activity != null)
                {
                    activity(null);
                }
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Create a new host with provided configuration and an Inversion of Control container.
        /// </summary>
        /// <remarks>
        /// The IoC container is also injected into default activator, so that <see cref="IDreamService"/> instances
        /// can be resolved from the container. The host configuration is provided to the container as a typed parameter.
        /// </remarks>
        /// <param name="config">Host configuration.</param>
        /// <param name="container">IoC Container.</param>
        public DreamHost(XDoc config, IContainer container)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            // read host settings
            string appDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
            int    limit        = config["connect-limit"].AsInt ?? 0;
            int    httpPort     = config["http-port"].AsInt ?? DEFAULT_PORT;
            AuthenticationSchemes authenticationScheme = AuthenticationSchemes.Anonymous;
            string authShemes = config["authentication-shemes"].AsText;

            if (!String.IsNullOrEmpty(authShemes))
            {
                try {
                    authenticationScheme = (AuthenticationSchemes)Enum.Parse(typeof(AuthenticationSchemes), authShemes, true);
                } catch (Exception e) {
                    _log.Warn(String.Format("invalid authetication scheme specified :{0}", authShemes), e);
                }
            }

            // get the authtoken for whitelisting dream.in.* query args
            _dreamInParamAuthtoken = config["dream.in.authtoken"].AsText;
            if (!string.IsNullOrEmpty(_dreamInParamAuthtoken))
            {
                _log.Debug("Host is configured in dream.in param authorizing mode");
            }

            // read ip-addresses
            var addresses = new List <string>();

            foreach (XDoc ip in config["host|ip"])
            {
                addresses.Add(ip.AsText);
            }
            if (addresses.Count == 0)
            {
                // if no addresses were supplied listen to all
                addresses.Add("*:" + httpPort);
            }

            // use default servername
            XUri publicUri = config["uri.public"].AsUri;

            if (publicUri == null)
            {
                // backwards compatibility
                publicUri = config["server-name"].AsUri;
                if (publicUri == null)
                {
                    foreach (IPAddress addr in Dns.GetHostAddresses(Dns.GetHostName()))
                    {
                        if (addr.AddressFamily == AddressFamily.InterNetwork)
                        {
                            XUri.TryParse("http://" + addr, out publicUri);
                        }
                    }
                    if (publicUri == null)
                    {
                        // failed to get an address out of dns, fall back to localhost
                        XUri.TryParse("http://localhost", out publicUri);
                    }
                }
                publicUri = publicUri.AtPath(config["server-path"].AsText ?? config["path-prefix"].AsText ?? string.Empty);
            }

            // create environment and initialize it
            _env = new DreamHostService(container);
            try {
                // initialize environment
                string apikey        = config["apikey"].AsText ?? StringUtil.CreateAlphaNumericKey(32);
                XDoc   serviceConfig = new XDoc("config");
                var    storageType   = config["storage/@type"].AsText ?? "local";
                if ("s3".EqualsInvariant(storageType))
                {
                    serviceConfig.Add(config["storage"]);
                }
                else
                {
                    serviceConfig.Elem("storage-dir", config["storage-dir"].AsText ?? config["service-dir"].AsText ?? appDirectory);
                }
                serviceConfig.Elem("apikey", apikey);
                serviceConfig.Elem("uri.public", publicUri);
                serviceConfig.Elem("connect-limit", limit);
                serviceConfig.Elem("guid", config["guid"].AsText);
                serviceConfig.AddAll(config["components"]);
                var memorize = config["memorize-aliases"];
                if (!memorize.IsEmpty)
                {
                    serviceConfig.Elem("memorize-aliases", memorize.AsBool);
                }
                _env.Initialize(serviceConfig);

                // initialize host plug
                _host = _env.Self.With("apikey", apikey);

                // load assemblies in 'services' folder
                string servicesFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "services");
                if (Directory.Exists(servicesFolder))
                {
                    // Note (arnec): Deprecated, but the suggested alternative really doesn't apply since we don't want to
                    // load services into a separate appdomain.
#pragma warning disable 618,612
                    AppDomain.CurrentDomain.AppendPrivatePath("services");
#pragma warning restore 618,612
                    foreach (string file in Directory.GetFiles(servicesFolder, "*.dll"))
                    {
                        // register assembly blueprints
                        DreamMessage response = _host.At("load").With("name", Path.GetFileNameWithoutExtension(file)).Post(new Result <DreamMessage>(TimeSpan.MaxValue)).Wait();
                        if (!response.IsSuccessful)
                        {
                            _log.WarnFormat("DreamHost: ERROR: assembly '{0}' failed to load", file);
                        }
                    }
                }

                // add acccess-points
                AddListener(new XUri(String.Format("http://{0}:{1}/", "localhost", httpPort)), authenticationScheme);

                // check if user prescribed a set of IP addresses to use
                if (addresses != null)
                {
                    // listen to custom addresses (don't use the supplied port info, we expect that to be part of the address)
                    foreach (string address in addresses)
                    {
                        if (!StringUtil.EqualsInvariantIgnoreCase(address, "localhost"))
                        {
                            AddListener(new XUri(String.Format("http://{0}/", address)), authenticationScheme);
                        }
                    }
                }
                else
                {
                    // add listeners for all known IP addresses
                    foreach (IPAddress address in Dns.GetHostAddresses(Dns.GetHostName()))
                    {
                        XUri uri = MakeUri(address, httpPort);
                        if (uri != null)
                        {
                            AddListener(uri, authenticationScheme);
                            try {
                                foreach (string alias in Dns.GetHostEntry(address).Aliases)
                                {
                                    AddListener(new XUri(String.Format("http://{0}:{1}/", alias, httpPort)), authenticationScheme);
                                }
                            } catch { }
                        }
                    }
                }
            } catch (Exception e) {
                if ((e is HttpListenerException) && e.Message.EqualsInvariant("Access is denied"))
                {
                    _log.ErrorExceptionMethodCall(e, "ctor", "insufficient privileges to create HttpListener, make sure the application runs with Administrator rights");
                }
                else
                {
                    _log.ErrorExceptionMethodCall(e, "ctor");
                }
                try {
                    _env.Deinitialize();
                } catch { }
                throw;
            }
        }
Esempio n. 7
0
        //--- Constructors ---

        /// <summary>
        /// Create new handler instance
        /// </summary>
        public HttpHandler()
        {
            if (_env == null)
            {
                lock (SyncRoot) {
                    if (_env == null)
                    {
                        _log.InfoMethodCall("Startup");
                        try {
                            _log.InfoMethodCall("ctor: initializing HttpHandler");
                            NameValueCollection settings = System.Configuration.ConfigurationManager.AppSettings;

                            // determine storage locations
                            string basePath    = HttpContext.Current.ApplicationInstance.Server.MapPath("~");
                            string storagePath = settings["storage-dir"] ?? settings["service-dir"];
                            if (string.IsNullOrEmpty(storagePath))
                            {
                                storagePath = Path.Combine(basePath, "storage");
                            }
                            else if (!Path.IsPathRooted(storagePath))
                            {
                                storagePath = Path.Combine(basePath, storagePath);
                            }

                            // read configuration
                            string apikey = settings["apikey"];
                            _uri                   = new XUri(settings["public-uri"] ?? settings["root-uri"] ?? "http://localhost/@api");
                            _minSimilarity         = _uri.MaxSimilarity;
                            _dreamInParamAuthtoken = settings["dream.in.authtoken"];

                            // start dreamhost
                            XDoc config = new XDoc("config")
                                          .Elem("guid", settings["guid"])
                                          .Elem("uri.public", _uri.ToString())
                                          .Elem("storage-dir", storagePath)
                                          .Elem("host-path", settings["host-path"])
                                          .Elem("connect-limit", settings["connect-limit"])
                                          .Elem("apikey", apikey);
                            IDreamEnvironment env = new DreamHostService();
                            env.Initialize(config);

                            // load assemblies in 'services' folder
                            string servicesFolder = settings["service-dir"] ?? Path.Combine("bin", "services");
                            if (!Path.IsPathRooted(servicesFolder))
                            {
                                servicesFolder = Path.Combine(basePath, servicesFolder);
                            }
                            _log.DebugFormat("examining services directory '{0}'", servicesFolder);
                            if (Directory.Exists(servicesFolder))
                            {
                                Plug host = env.Self.With("apikey", apikey);
                                foreach (string file in Directory.GetFiles(servicesFolder, "*.dll"))
                                {
                                    string assembly = Path.GetFileNameWithoutExtension(file);
                                    _log.DebugFormat("attempting to load '{0}'", assembly);

                                    // register assembly blueprints
                                    DreamMessage response = host.At("load").With("name", assembly).Post(new Result <DreamMessage>(TimeSpan.MaxValue)).Wait();
                                    if (!response.IsSuccessful)
                                    {
                                        _log.WarnFormat("DreamHost: ERROR: assembly '{0}' failed to load", file);
                                    }
                                }
                            }
                            else
                            {
                                _log.WarnFormat("DreamHost: WARN: no services directory '{0}'", servicesFolder);
                            }


                            // execute script
                            string scriptFilename = settings["script"];
                            if (!string.IsNullOrEmpty(scriptFilename))
                            {
                                string filename = scriptFilename;
                                if (!Path.IsPathRooted(filename))
                                {
                                    filename = Path.Combine(basePath, filename);
                                }

                                // execute xml script file
                                XDoc script = XDocFactory.LoadFrom(filename, MimeType.XML);
                                Plug host   = env.Self.With("apikey", apikey);
                                host.At("execute").Post(script);
                            }

                            // register plug factory for this uri
                            Plug.AddEndpoint(this);

                            // set _env variable so other constructors don't initialize it anymore
                            _env = env;
                        } catch (Exception e) {
                            _log.ErrorExceptionMethodCall(e, "ctor");
                            throw;
                        }
                    }
                }
            }
        }
        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;
                }
            }
        }