Example #1
0
        /// <summary>
        /// Retrieves web.config for a given Azure Website.
        /// </summary>
        /// <returns>XML document with the contents of web.config, or <c>null</c> if it could not be retrieved.</returns>
        private async Task <XDocument> GetWebConfig(AzureWebSiteInfo webSite)
        {
            var publishXml = await GetPublishXml(webSite);

            if (publishXml == null)
            {
                return(null);
            }

            // Get FTP publish URL and credentials from publish settings.

            var publishProfile = publishXml.Elements("publishData").Elements("publishProfile").FirstOrDefault(el => (string)el.Attribute("publishMethod") == "FTP");

            if (publishProfile == null)
            {
                return(null);
            }

            var publishUrl = (string)publishProfile.Attribute("publishUrl");
            var userName   = (string)publishProfile.Attribute("userName");
            var userPwd    = (string)publishProfile.Attribute("userPWD");

            if (publishUrl == null || userName == null || userPwd == null)
            {
                return(null);
            }

            // Get web.config for the site via FTP.

            if (!publishUrl.EndsWith("/", StringComparison.Ordinal))
            {
                publishUrl += "/";
            }
            publishUrl += "web.config";

            if (!Uri.TryCreate(publishUrl, UriKind.Absolute, out var webConfigUri))
            {
                return(null);
            }

            var request = WebRequest.Create(webConfigUri) as FtpWebRequest;

            // Check that this is actually an FTP request, in case we get some valid but weird URL back.
            if (request == null)
            {
                return(null);
            }
            request.Credentials = new NetworkCredential(userName, userPwd);

            using (var response = await request.GetResponseAsync())
                using (var stream = response.GetResponseStream())
                {
                    // There is no XDocument.LoadAsync, but we want the networked I/O at least to be async, even if parsing is not.
                    var xmlData = new MemoryStream();
                    await stream.CopyToAsync(xmlData);

                    xmlData.Position = 0;
                    return(XDocument.Load(xmlData));
                }
        }
        private async Task <bool> AttachWorker(AzureWebSiteInfo webSite)
        {
            using (new WaitDialog("Azure remote debugging", "Attaching to Azure web site at " + webSite.Uri, _serviceProvider, showProgress: true)) {
                // Get path (relative to site URL) for the debugger endpoint.
                XDocument webConfig;
                try {
                    webConfig = await GetWebConfig(webSite);
                } catch (WebException) {
                    return(false);
                } catch (IOException) {
                    return(false);
                } catch (XmlException) {
                    return(false);
                }
                if (webConfig == null)
                {
                    return(false);
                }

                var path =
                    (from add in webConfig.Elements("configuration").Elements("system.webServer").Elements("handlers").Elements("add")
                     let type = (string)add.Attribute("type")
                                where type != null
                                let components = type.Split(',')
                                                 where components[0].Trim() == "Microsoft.PythonTools.Debugger.WebSocketProxy"
                                                 select(string) add.Attribute("path")
                    ).FirstOrDefault();
                if (path == null)
                {
                    return(false);
                }

                var secret =
                    (from add in webConfig.Elements("configuration").Elements("appSettings").Elements("add")
                     where (string)add.Attribute("key") == "WSGI_PTVSD_SECRET"
                     select(string) add.Attribute("value")
                    ).FirstOrDefault();
                if (secret == null)
                {
                    return(false);
                }

                try {
                    AttachDebugger(new UriBuilder(webSite.Uri)
                    {
                        Scheme = "wss", Port = -1, Path = path, UserName = secret
                    }.Uri);
                } catch (Exception ex) {
                    // If we got to this point, the attach logic in debug engine will catch exceptions, display proper error message and
                    // ask the user to retry, so the only case where we actually get here is if user canceled on error. If this is the case,
                    // we don't want to pop any additional error messages, so always return true, but log the error in the Output window.
                    var output = OutputWindowRedirector.GetGeneral(_serviceProvider);
                    output.WriteErrorLine("Failed to attach to Azure web site: " + ex.Message);
                    output.ShowAndActivate();
                }
                return(true);
            }
        }
Example #3
0
        private async Task <bool> AttachWorker(AzureWebSiteInfo webSite)
        {
            using (new WaitDialog(
                       Project.SR.GetString(Project.SR.AzureRemoteDebugWaitCaption),
                       Project.SR.GetString(Project.SR.AzureRemoteDebugWaitMessage, webSite.Uri),
                       NodejsPackage.Instance,
                       showProgress: true)) {
                // Get path (relative to site URL) for the debugger endpoint.
                XDocument webConfig;
                try {
                    webConfig = await GetWebConfig(webSite);
                } catch (WebException) {
                    return(false);
                } catch (IOException) {
                    return(false);
                } catch (XmlException) {
                    return(false);
                }
                if (webConfig == null)
                {
                    return(false);
                }

                var path =
                    (from add in webConfig.Elements("configuration").Elements("system.webServer").Elements("handlers").Elements("add")
                     let type = (string)add.Attribute("type")
                                where type != null
                                let components = type.Split(',')
                                                 where components[0].Trim() == "Microsoft.NodejsTools.Debugger.WebSocketProxy"
                                                 select(string) add.Attribute("path")
                    ).FirstOrDefault();
                if (path == null)
                {
                    return(false);
                }

                try {
                    AttachDebugger(new UriBuilder(webSite.Uri)
                    {
                        Scheme = "wss", Port = -1, Path = path
                    }.Uri);
                } catch (Exception ex) {
                    // If we got to this point, the attach logic in debug engine will catch exceptions, display proper error message and
                    // ask the user to retry, so the only case where we actually get here is if user canceled on error. If this is the case,
                    // we don't want to pop any additional error messages, so always return true, but log the error in the Output window.
                    var output = OutputWindowRedirector.GetGeneral(NodejsPackage.Instance);
                    output.WriteErrorLine(Project.SR.GetString(Project.SR.AzureRemoveDebugCouldNotAttachToWebsiteExceptionErrorMessage, ex.Message));
                    output.ShowAndActivate();
                }
                return(true);
            }
        }
        /// <summary>
        /// Retrieves the publish settings file (.pubxml) for the given Azure web site.
        /// </summary>
        /// <returns>XML document with the contents of .pubxml, or <c>null</c> if it could not be retrieved.</returns>
        private async Task<XDocument> GetPublishXml(AzureWebSiteInfo webSiteInfo) {
            // To build the publish settings request URL, we need to know subscription ID, site name, and web region to which it belongs,
            // but we only have subscription ID and the public URL of the site at this point. Use the Azure web site service to look up
            // the site from those two, and retrieve the missing info.

            // The code below must avoid doing anything that would result in types from the Azure Tools contract assemblies being
            // referenced in any way in signatures of any members of any classes in this assembly. Because the contract assembly can
            // be be missing (if Azure SDK is not installed), such references will cause Reflection to break, which will break MEF
            // and block all our MEF exports. The main danger is classes generated by the compiler to support constructs such as
            // lambdas and await. To that extent, the following are not allowed in the code below:
            //
            //   - local variables of Azure types (become fields if captured by a lambda or used across await);
            //   - lambda arguments of Azure types (become arguments on generated method), and LINQ expressions that would implicitly
            //     produce such lambdas;
            //   - await on a Task that has result of an Azure type (produces a field of type TaskAwaiter<TResult>);
            //
            // To make it easier to verify, "var" and LINQ syntactic sugar should be avoided completely when dealing with Azure interfaces,
            // and all lambdas should have the types of arguments explicitly specified. For "await", cast the return type of any
            // Azure-type-returning async method to untyped Task first before awaiting, then use an explicit cast to Task<T> to read Result.
            // The only mentions of Azure types in the body of the method should be in casts.

            object webSiteServices = _serviceProvider.GetService(typeof(IVsAzureServices));
            if (webSiteServices == null) {
                return null;
            }

            object webSiteService = ((IVsAzureServices)webSiteServices).GetAzureWebSitesService();
            if (webSiteService == null) {
                return null;
            }

            Task getSubscriptionsAsyncTask = (Task)((IAzureWebSitesService)webSiteService).GetSubscriptionsAsync();
            await getSubscriptionsAsyncTask;

            IEnumerable<object> subscriptions = ((Task<List<IAzureSubscription>>)getSubscriptionsAsyncTask).Result;
            object subscription = subscriptions.FirstOrDefault((object sub) => ((IAzureSubscription)sub).SubscriptionId == webSiteInfo.SubscriptionId);
            if (subscription == null) {
                return null;
            }

            Task getResourcesAsyncTask = (Task)((IAzureSubscription)subscription).GetResourcesAsync(false);
            await getResourcesAsyncTask;

            IEnumerable<object> resources = ((Task<List<IAzureResource>>)getResourcesAsyncTask).Result;
            object webSite = resources.FirstOrDefault((object res) => {
                IAzureWebSite ws = res as IAzureWebSite;
                if (ws == null) {
                    return false;
                }
                Uri browseUri;
                Uri.TryCreate(ws.BrowseURL, UriKind.Absolute, out browseUri);
                return browseUri != null && browseUri.Equals(webSiteInfo.Uri);
            });
            if (webSite == null) {
                return null;
            }

            // Prepare a web request to get the publish settings.
            // See http://msdn.microsoft.com/en-us/library/windowsazure/dn166996.aspx
            string requestPath = string.Format(
                "{0}/services/WebSpaces/{1}/sites/{2}/publishxml",
                ((IAzureSubscription)subscription).SubscriptionId,
                ((IAzureWebSite)webSite).WebSpace,
                ((IAzureWebSite)webSite).Name);
            Uri requestUri = new Uri(((IAzureSubscription)subscription).ServiceManagementEndpointUri, requestPath);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
            request.Method = "GET";
            request.ContentType = "application/xml";
            request.Headers.Add("x-ms-version", "2010-10-28");

            // Set up authentication for the request, depending on whether the associated subscription context is 
            // account-based or certificate-based.
            object context = ((IAzureSubscription)subscription).AzureCredentials;
            if (context is IAzureAuthenticationCertificateSubscriptionContext) {
                X509Certificate2 cert = await ((IAzureAuthenticationCertificateSubscriptionContext)context).AuthenticationCertificate.GetCertificateFromStoreAsync();
                request.ClientCertificates.Add(cert);
            } else if (context is IAzureUserAccountSubscriptionContext) {
                string authHeader = await ((IAzureUserAccountSubscriptionContext)context).GetAuthenticationHeaderAsync(false);
                request.Headers.Add(HttpRequestHeader.Authorization, authHeader);
            } else {
                return null;
            }

            using (WebResponse response = await request.GetResponseAsync())
            using (Stream stream = response.GetResponseStream()) {
                // There is no XDocument.LoadAsync, but we want the networked I/O at least to be async, even if parsing is not.
                Stream xmlData = new MemoryStream();
                await stream.CopyToAsync(xmlData);
                xmlData.Position = 0;
                return XDocument.Load(xmlData);
            }
        }
        /// <summary>
        /// Retrieves web.config for a given Azure web site.
        /// </summary>
        /// <returns>XML document with the contents of web.config, or <c>null</c> if it could not be retrieved.</returns>
        private async Task<XDocument> GetWebConfig(AzureWebSiteInfo webSite) {
            var publishXml = await GetPublishXml(webSite);
            if (publishXml == null) {
                return null;
            }

            // Get FTP publish URL and credentials from publish settings.

            var publishProfile = publishXml.Elements("publishData").Elements("publishProfile").FirstOrDefault(el => (string)el.Attribute("publishMethod") == "FTP");
            if (publishProfile == null) {
                return null;
            }

            var publishUrl = (string)publishProfile.Attribute("publishUrl");
            var userName = (string)publishProfile.Attribute("userName");
            var userPwd = (string)publishProfile.Attribute("userPWD");
            if (publishUrl == null || userName == null || userPwd == null) {
                return null;
            }

            // Get web.config for the site via FTP.

            if (!publishUrl.EndsWith("/")) {
                publishUrl += "/";
            }
            publishUrl += "web.config";

            Uri webConfigUri;
            if (!Uri.TryCreate(publishUrl, UriKind.Absolute, out webConfigUri)) {
                return null;
            }

            var request = WebRequest.Create(webConfigUri) as FtpWebRequest;
            // Check that this is actually an FTP request, in case we get some valid but weird URL back.
            if (request == null) {
                return null;
            }
            request.Credentials = new NetworkCredential(userName, userPwd);

            using (var response = await request.GetResponseAsync())
            using (var stream = response.GetResponseStream()) {
                // There is no XDocument.LoadAsync, but we want the networked I/O at least to be async, even if parsing is not.
                var xmlData = new MemoryStream();
                await stream.CopyToAsync(xmlData);
                xmlData.Position = 0;
                return XDocument.Load(xmlData);
            }
        }
        private async Task<bool> AttachWorker(AzureWebSiteInfo webSite) {
            using (new WaitDialog("Azure remote debugging", "Attaching to Azure web site at " + webSite.Uri, _serviceProvider, showProgress: true)) {
                // Get path (relative to site URL) for the debugger endpoint.
                XDocument webConfig;
                try {
                    webConfig = await GetWebConfig(webSite);
                } catch (WebException) {
                    return false;
                } catch (IOException) {
                    return false;
                } catch (XmlException) {
                    return false;
                }
                if (webConfig == null) {
                    return false;
                }

                var path =
                    (from add in webConfig.Elements("configuration").Elements("system.webServer").Elements("handlers").Elements("add")
                     let type = (string)add.Attribute("type")
                     where type != null
                     let components = type.Split(',')
                     where components[0].Trim() == "Microsoft.PythonTools.Debugger.WebSocketProxy"
                     select (string)add.Attribute("path")
                    ).FirstOrDefault();
                if (path == null) {
                    return false;
                }

                var secret =
                    (from add in webConfig.Elements("configuration").Elements("appSettings").Elements("add")
                     where (string)add.Attribute("key") == "WSGI_PTVSD_SECRET"
                     select (string)add.Attribute("value")
                    ).FirstOrDefault();
                if (secret == null) {
                    return false;
                }

                try {
                    AttachDebugger(new UriBuilder(webSite.Uri) { Scheme = "wss", Port = -1, Path = path, UserName = secret }.Uri);
                } catch (Exception ex) {
                    // If we got to this point, the attach logic in debug engine will catch exceptions, display proper error message and
                    // ask the user to retry, so the only case where we actually get here is if user canceled on error. If this is the case,
                    // we don't want to pop any additional error messages, so always return true, but log the error in the Output window.
                    var output = OutputWindowRedirector.GetGeneral(_serviceProvider);
                    output.WriteErrorLine("Failed to attach to Azure web site: " + ex.Message);
                    output.ShowAndActivate();
                }
                return true;
            }
        }
Example #7
0
        /// <summary>
        /// Retrieves the publish settings file (.pubxml) for the given Azure Website.
        /// </summary>
        /// <returns>XML document with the contents of .pubxml, or <c>null</c> if it could not be retrieved.</returns>
        private async Task <XDocument> GetPublishXml(AzureWebSiteInfo webSiteInfo)
        {
            // To build the publish settings request URL, we need to know subscription ID, site name, and web region to which it belongs,
            // but we only have subscription ID and the public URL of the site at this point. Use the Azure Website service to look up
            // the site from those two, and retrieve the missing info.

            IVsAzureServices webSiteServices = new VsAzureServicesShim(NodejsPackage.GetGlobalService(_azureServicesType));

            if (webSiteServices == null)
            {
                return(null);
            }

            var webSiteService = webSiteServices.GetAzureWebSitesService();

            if (webSiteService == null)
            {
                return(null);
            }

            var subscriptions = await webSiteService.GetSubscriptionsAsync();

            var subscription = subscriptions.FirstOrDefault(sub => sub.SubscriptionId == webSiteInfo.SubscriptionId);

            if (subscription == null)
            {
                return(null);
            }

            var resources = await subscription.GetResourcesAsync(false);

            var webSite = resources.OfType <IAzureWebSite>().FirstOrDefault(ws => {
                Uri browseUri;
                Uri.TryCreate(ws.BrowseURL, UriKind.Absolute, out browseUri);
                return(browseUri != null && browseUri.Equals(webSiteInfo.Uri));
            });

            if (webSite == null)
            {
                return(null);
            }

            // Prepare a web request to get the publish settings.
            // See http://msdn.microsoft.com/en-us/library/windowsazure/dn166996.aspx
            string requestPath = string.Format(CultureInfo.InvariantCulture,
                                               "{0}/services/WebSpaces/{1}/sites/{2}/publishxml",
                                               subscription.SubscriptionId,
                                               webSite.WebSpace,
                                               webSite.Name);
            Uri            requestUri = new Uri(((IAzureSubscription)subscription).ServiceManagementEndpointUri, requestPath);
            HttpWebRequest request    = (HttpWebRequest)WebRequest.Create(requestUri);

            request.Method      = "GET";
            request.ContentType = "application/xml";
            request.Headers.Add("x-ms-version", "2010-10-28");

            // Set up authentication for the request, depending on whether the associated subscription context is
            // account-based or certificate-based.
            object context     = subscription.AzureCredentials;
            var    certContext = context as IAzureAuthenticationCertificateSubscriptionContext;

            if (certContext != null)
            {
                var cert = await certContext.AuthenticationCertificate.GetCertificateFromStoreAsync();

                request.ClientCertificates.Add(cert);
            }
            else
            {
                var accountCountext = context as IAzureUserAccountSubscriptionContext;
                if (accountCountext != null)
                {
                    string authHeader = await accountCountext.GetAuthenticationHeaderAsync(false);

                    request.Headers.Add(HttpRequestHeader.Authorization, authHeader);
                }
                else
                {
                    return(null);
                }
            }

            using (WebResponse response = await request.GetResponseAsync())
                using (Stream stream = response.GetResponseStream()) {
                    // There is no XDocument.LoadAsync, but we want the networked I/O at least to be async, even if parsing is not.
                    Stream xmlData = new MemoryStream();
                    await stream.CopyToAsync(xmlData);

                    xmlData.Position = 0;
                    return(XDocument.Load(xmlData));
                }
        }
        /// <summary>
        /// Retrieves the publish settings file (.pubxml) for the given Azure Website.
        /// </summary>
        /// <returns>XML document with the contents of .pubxml, or <c>null</c> if it could not be retrieved.</returns>
        private async Task<XDocument> GetPublishXml(AzureWebSiteInfo webSiteInfo) {
            // To build the publish settings request URL, we need to know subscription ID, site name, and web region to which it belongs,
            // but we only have subscription ID and the public URL of the site at this point. Use the Azure Website service to look up
            // the site from those two, and retrieve the missing info.

            IVsAzureServices webSiteServices = new VsAzureServicesShim(NodejsPackage.GetGlobalService(_azureServicesType));
            if (webSiteServices == null) {
                return null;
            }

            var webSiteService = webSiteServices.GetAzureWebSitesService();
            if (webSiteService == null) {
                return null;
            }

            var subscriptions = await webSiteService.GetSubscriptionsAsync();
            var subscription = subscriptions.FirstOrDefault(sub => sub.SubscriptionId == webSiteInfo.SubscriptionId);
            if (subscription == null) {
                return null;
            }

            var resources = await subscription.GetResourcesAsync(false);
            var webSite = resources.OfType<IAzureWebSite>().FirstOrDefault(ws => {
                Uri browseUri;
                Uri.TryCreate(ws.BrowseURL, UriKind.Absolute, out browseUri);
                return browseUri != null && browseUri.Equals(webSiteInfo.Uri);
            });
            if (webSite == null) {
                return null;
            }

            // Prepare a web request to get the publish settings.
            // See http://msdn.microsoft.com/en-us/library/windowsazure/dn166996.aspx
            string requestPath = string.Format(
                "{0}/services/WebSpaces/{1}/sites/{2}/publishxml",
                subscription.SubscriptionId,
                webSite.WebSpace,
                webSite.Name);
            Uri requestUri = new Uri(((IAzureSubscription)subscription).ServiceManagementEndpointUri, requestPath);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
            request.Method = "GET";
            request.ContentType = "application/xml";
            request.Headers.Add("x-ms-version", "2010-10-28");

            // Set up authentication for the request, depending on whether the associated subscription context is 
            // account-based or certificate-based.
            object context = subscription.AzureCredentials;
            var certContext = context as IAzureAuthenticationCertificateSubscriptionContext;
            if (certContext != null) {
                var cert = await certContext.AuthenticationCertificate.GetCertificateFromStoreAsync();
                request.ClientCertificates.Add(cert);
            } else {
                var accountCountext = context as IAzureUserAccountSubscriptionContext;
                if (accountCountext != null) {
                    string authHeader = await accountCountext.GetAuthenticationHeaderAsync(false);
                    request.Headers.Add(HttpRequestHeader.Authorization, authHeader);
                } else {
                    return null;
                }
            }

            using (WebResponse response = await request.GetResponseAsync())
            using (Stream stream = response.GetResponseStream()) {
                // There is no XDocument.LoadAsync, but we want the networked I/O at least to be async, even if parsing is not.
                Stream xmlData = new MemoryStream();
                await stream.CopyToAsync(xmlData);
                xmlData.Position = 0;
                return XDocument.Load(xmlData);
            }
        }
        /// <summary>
        /// Retrieves the publish settings file (.pubxml) for the given Azure web site.
        /// </summary>
        /// <returns>XML document with the contents of .pubxml, or <c>null</c> if it could not be retrieved.</returns>
        private async Task <XDocument> GetPublishXml(AzureWebSiteInfo webSiteInfo)
        {
            // To build the publish settings request URL, we need to know subscription ID, site name, and web region to which it belongs,
            // but we only have subscription ID and the public URL of the site at this point. Use the Azure web site service to look up
            // the site from those two, and retrieve the missing info.

            // The code below must avoid doing anything that would result in types from the Azure Tools contract assemblies being
            // referenced in any way in signatures of any members of any classes in this assembly. Because the contract assembly can
            // be be missing (if Azure SDK is not installed), such references will cause Reflection to break, which will break MEF
            // and block all our MEF exports. The main danger is classes generated by the compiler to support constructs such as
            // lambdas and await. To that extent, the following are not allowed in the code below:
            //
            //   - local variables of Azure types (become fields if captured by a lambda or used across await);
            //   - lambda arguments of Azure types (become arguments on generated method), and LINQ expressions that would implicitly
            //     produce such lambdas;
            //   - await on a Task that has result of an Azure type (produces a field of type TaskAwaiter<TResult>);
            //
            // To make it easier to verify, "var" and LINQ syntactic sugar should be avoided completely when dealing with Azure interfaces,
            // and all lambdas should have the types of arguments explicitly specified. For "await", cast the return type of any
            // Azure-type-returning async method to untyped Task first before awaiting, then use an explicit cast to Task<T> to read Result.
            // The only mentions of Azure types in the body of the method should be in casts.

            object webSiteServices = _serviceProvider.GetService(typeof(IVsAzureServices));

            if (webSiteServices == null)
            {
                return(null);
            }

            object webSiteService = ((IVsAzureServices)webSiteServices).GetAzureWebSitesService();

            if (webSiteService == null)
            {
                return(null);
            }

            Task  getSubscriptionsAsyncTask = (Task)((IAzureWebSitesService)webSiteService).GetSubscriptionsAsync();
            await getSubscriptionsAsyncTask;

            IEnumerable <object> subscriptions = ((Task <List <IAzureSubscription> >)getSubscriptionsAsyncTask).Result;
            object subscription = subscriptions.FirstOrDefault((object sub) => ((IAzureSubscription)sub).SubscriptionId == webSiteInfo.SubscriptionId);

            if (subscription == null)
            {
                return(null);
            }

            Task  getResourcesAsyncTask = (Task)((IAzureSubscription)subscription).GetResourcesAsync(false);
            await getResourcesAsyncTask;

            IEnumerable <object> resources = ((Task <List <IAzureResource> >)getResourcesAsyncTask).Result;
            object webSite = resources.FirstOrDefault((object res) => {
                IAzureWebSite ws = res as IAzureWebSite;
                if (ws == null)
                {
                    return(false);
                }
                Uri browseUri;
                Uri.TryCreate(ws.BrowseURL, UriKind.Absolute, out browseUri);
                return(browseUri != null && browseUri.Equals(webSiteInfo.Uri));
            });

            if (webSite == null)
            {
                return(null);
            }

            // Prepare a web request to get the publish settings.
            // See http://msdn.microsoft.com/en-us/library/windowsazure/dn166996.aspx
            string requestPath = string.Format(
                "{0}/services/WebSpaces/{1}/sites/{2}/publishxml",
                ((IAzureSubscription)subscription).SubscriptionId,
                ((IAzureWebSite)webSite).WebSpace,
                ((IAzureWebSite)webSite).Name);
            Uri            requestUri = new Uri(((IAzureSubscription)subscription).ServiceManagementEndpointUri, requestPath);
            HttpWebRequest request    = (HttpWebRequest)WebRequest.Create(requestUri);

            request.Method      = "GET";
            request.ContentType = "application/xml";
            request.Headers.Add("x-ms-version", "2010-10-28");

            // Set up authentication for the request, depending on whether the associated subscription context is
            // account-based or certificate-based.
            object context = ((IAzureSubscription)subscription).AzureCredentials;

            if (context is IAzureAuthenticationCertificateSubscriptionContext)
            {
                X509Certificate2 cert = await((IAzureAuthenticationCertificateSubscriptionContext)context).AuthenticationCertificate.GetCertificateFromStoreAsync();
                request.ClientCertificates.Add(cert);
            }
            else if (context is IAzureUserAccountSubscriptionContext)
            {
                string authHeader = await((IAzureUserAccountSubscriptionContext)context).GetAuthenticationHeaderAsync(false);
                request.Headers.Add(HttpRequestHeader.Authorization, authHeader);
            }
            else
            {
                return(null);
            }

            using (WebResponse response = await request.GetResponseAsync())
                using (Stream stream = response.GetResponseStream()) {
                    // There is no XDocument.LoadAsync, but we want the networked I/O at least to be async, even if parsing is not.
                    Stream xmlData = new MemoryStream();
                    await stream.CopyToAsync(xmlData);

                    xmlData.Position = 0;
                    return(XDocument.Load(xmlData));
                }
        }
        private async Task<bool> AttachWorker(AzureWebSiteInfo webSite) {
            using (new WaitDialog(
                Resources.AzureRemoteDebugWaitCaption,
                string.Format(CultureInfo.CurrentCulture, Resources.AzureRemoteDebugWaitMessage, webSite.Uri),
                NodejsPackage.Instance,
                showProgress: true)) {
                // Get path (relative to site URL) for the debugger endpoint.
                XDocument webConfig;
                try {
                    webConfig = await GetWebConfig(webSite);
                } catch (WebException) {
                    return false;
                } catch (IOException) {
                    return false;
                } catch (XmlException) {
                    return false;
                }
                if (webConfig == null) {
                    return false;
                }

                var path =
                    (from add in webConfig.Elements("configuration").Elements("system.webServer").Elements("handlers").Elements("add")
                     let type = (string)add.Attribute("type")
                     where type != null
                     let components = type.Split(',')
                     where components[0].Trim() == "Microsoft.NodejsTools.Debugger.WebSocketProxy"
                     select (string)add.Attribute("path")
                    ).FirstOrDefault();
                if (path == null) {
                    return false;
                }

                try {
                    AttachDebugger(new UriBuilder(webSite.Uri) { Scheme = "wss", Port = -1, Path = path }.Uri);
                } catch (Exception ex) {
                    // If we got to this point, the attach logic in debug engine will catch exceptions, display proper error message and
                    // ask the user to retry, so the only case where we actually get here is if user canceled on error. If this is the case,
                    // we don't want to pop any additional error messages, so always return true, but log the error in the Output window.
                    var output = OutputWindowRedirector.GetGeneral(NodejsPackage.Instance);
                    output.WriteErrorLine(string.Format(CultureInfo.CurrentCulture, Resources.AzureRemoveDebugCouldNotAttachToWebsiteExceptionErrorMessage, ex.Message));
                    output.ShowAndActivate();
                }
                return true;
            }
        }