public AgentDocument PerformAction(AgentSession session, AgentAction action, int timeout)
        {
            AgentDocument document;

            // these resources must be released if any exceptions occur
            HttpWebResponse actionResponse = null;
            Stream          responseStream = null;

            try
            {
                // set up the request and its headers
                var actionRequest = (HttpWebRequest)WebRequest.Create(new Uri(action.WebURL));

                if (!string.IsNullOrEmpty(action.Referer))
                {
                    actionRequest.Referer = action.Referer;
                }

                if (action.IsPost)
                {
                    actionRequest.Method = "POST";
                }
                else
                {
                    actionRequest.Method = "GET";
                }

                actionRequest.ContentType = "application/x-www-form-urlencoded";

                if (action.UserAgent == null)
                {
                    actionRequest.UserAgent =
                        "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0";
                }
                else
                {
                    actionRequest.UserAgent = action.UserAgent;
                }

                actionRequest.Accept =
                    "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1";
                actionRequest.AllowAutoRedirect = action.AllowRedirect;
                actionRequest.KeepAlive         = false;

                if (!string.IsNullOrEmpty(action.ProxyAddress))
                {
                    var parts = action.ProxyAddress.Split(':');

                    if (parts.Length == 2)
                    {
                        actionRequest.Proxy = new WebProxy(parts[0], int.Parse(parts[1]));
                    }
                    else
                    {
                        actionRequest.Proxy = new WebProxy(parts[0]);
                    }
                }

                if (timeout > 0)
                {
                    actionRequest.Timeout = timeout;
                }

                actionRequest.Headers.Add("Accept-Language", "en-us,en;q=0.5");
                actionRequest.ProtocolVersion = new Version(action.Version);

                actionRequest.CookieContainer = session.Cookies;

                if (action.IsPost)
                {
                    if (action.Attachment == null)
                    {
                        WriteContent(action, actionRequest);
                    }
                    else
                    {
                        WriteFileAttachmentContent(action, actionRequest);
                    }
                }

                document = new AgentDocument();
                actionRequest.ServicePoint.Expect100Continue = false;
                actionResponse = (HttpWebResponse)actionRequest.GetResponse();

                document.RedirectUri = action.AllowRedirect
                                                               ? actionResponse.ResponseUri.ToString()
                                                               : actionResponse.GetResponseHeader("Location");

                responseStream = actionResponse.GetResponseStream();
                Debug.Assert(responseStream != null, "responseStream != null");
                var responseBuilder = new StringWriter();

                var buffer = new byte[1024];

                int n;
                do
                {
                    n = responseStream.Read(buffer, 0, buffer.Length);

                    var charBuffer = new char[buffer.Length];
                    for (var i = 0; i < buffer.Length; i++)
                    {
                        charBuffer[i] = (char)buffer[i];
                    }

                    responseBuilder.Write(charBuffer, 0, n);
                } while (n > 0);

                document.ResponseString = responseBuilder.GetStringBuilder().ToString();
                responseBuilder.Close();
                responseBuilder.Dispose();

                document.Uri = actionResponse.ResponseUri.ToString();
            }
            finally
            {
                try
                {
                    responseStream?.Close();
                }
                catch
                {
                    // ignored
                }

                try
                {
                    actionResponse?.Close();
                }
                catch
                {
                    // ignored
                }
            }

            return(document);
        }
 // *** Public API ***
 public AgentDocument PerformAction(AgentSession session, AgentAction action)
 {
     return(PerformAction(session, action, 0));
 }