diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/BasicHtmlWebResponseObject.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs similarity index 87% rename from src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/BasicHtmlWebResponseObject.cs rename to src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs index d5b3d1cd88..f6f3fbc35c 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/BasicHtmlWebResponseObject.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs @@ -1,4 +1,4 @@ -/********************************************************************++ +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ @@ -9,7 +9,6 @@ using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Collections.Generic; -using Microsoft.Win32; using System.Diagnostics; namespace Microsoft.PowerShell.Commands @@ -17,7 +16,7 @@ namespace Microsoft.PowerShell.Commands /// /// Response object for html content without DOM parsing /// - public class BasicHtmlWebResponseObject : WebResponseObject + public partial class BasicHtmlWebResponseObject : WebResponseObject { #region Properties @@ -109,30 +108,6 @@ namespace Microsoft.PowerShell.Commands #endregion Properties - #region Constructors - - /// - /// Constructor for HtmlWebResponseObject - /// - /// - public BasicHtmlWebResponseObject(WebResponse response) - : this(response, null) { } - - /// - /// Constructor for HtmlWebResponseObject with memory stream - /// - /// - /// - public BasicHtmlWebResponseObject(WebResponse response, Stream contentStream) - : base(response, contentStream) - { - EnsureHtmlParser(); - InitializeContent(); - InitializeRawContent(response); - } - - #endregion Constructors - #region Private Fields private static Regex _tagRegex; @@ -146,13 +121,6 @@ namespace Microsoft.PowerShell.Commands #region Methods - private void InitializeRawContent(WebResponse baseResponse) - { - StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); - raw.Append(Content); - this.RawContent = raw.ToString(); - } - private void EnsureHtmlParser() { @@ -204,8 +172,7 @@ namespace Microsoft.PowerShell.Commands return elementObject; } - - + private void ParseAttributes(string outerHtml, PSObject elementObject) { // We might get an empty input for a directive from the HTML file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ContentHelper.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/ContentHelper.Common.cs similarity index 71% rename from src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ContentHelper.cs rename to src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/ContentHelper.Common.cs index e2b0f76f14..a7e20981ac 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ContentHelper.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/ContentHelper.Common.cs @@ -1,20 +1,14 @@ -/********************************************************************++ +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System; -using System.Collections.Generic; -using System.Linq; using System.Text; using Microsoft.Win32; -using System.Net; -using System.Reflection; -using System.Globalization; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { - internal static class ContentHelper + internal static partial class ContentHelper { #region Constants @@ -46,32 +40,6 @@ namespace Microsoft.PowerShell.Commands return CheckIsJson(contentType); } - internal static Encoding GetEncoding(WebResponse response) - { - string characterSet = null; - HttpWebResponse httpResponse = response as HttpWebResponse; - if (null != httpResponse) - { - characterSet = httpResponse.CharacterSet; - } - - return GetEncodingOrDefault(characterSet); - } - - // Gets the content type with safe fallback - in the situation - // of FTPWebResponse that retuns NotImplemented. - internal static string GetContentType(WebResponse response) - { - string contentType = null; - try - { - contentType = response.ContentType; - } - catch (NotImplementedException) { } - - return contentType; - } - internal static Encoding GetEncodingOrDefault(string characterSet) { // get the name of the codepage to use for response content @@ -84,7 +52,8 @@ namespace Microsoft.PowerShell.Commands } catch (ArgumentException) { - encoding = Encoding.GetEncoding(null); + // 0, default code page + encoding = Encoding.GetEncoding(0); } return encoding; @@ -176,33 +145,7 @@ namespace Microsoft.PowerShell.Commands return (isJson); } - - internal static StringBuilder GetRawContentHeader(WebResponse baseResponse) - { - StringBuilder raw = new StringBuilder(); - - // add protocol and status line - string protocol = WebResponseHelper.GetProtocol(baseResponse); - if (!String.IsNullOrEmpty(protocol)) - { - int statusCode = WebResponseHelper.GetStatusCode(baseResponse); - string statusDescription = WebResponseHelper.GetStatusDescription(baseResponse); - raw.AppendFormat("{0} {1} {2}", protocol, statusCode, statusDescription); - raw.AppendLine(); - } - - // add headers - foreach (string key in baseResponse.Headers.AllKeys) - { - string value = baseResponse.Headers[key]; - raw.AppendFormat("{0}: {1}", key, value); - raw.AppendLine(); - } - - raw.AppendLine(); - return raw; - } - + #endregion Internal Helper Methods } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/HtmlWebResponseObject.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HtmlWebResponseObject.Common.cs similarity index 91% rename from src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/HtmlWebResponseObject.cs rename to src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HtmlWebResponseObject.Common.cs index ee5e174a80..e07f4477be 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/HtmlWebResponseObject.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HtmlWebResponseObject.Common.cs @@ -1,16 +1,14 @@ -/********************************************************************++ +#if !CORECLR + +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System; using System.Management.Automation; -using System.Net; -using System.IO; -using System.Text; using System.Text.RegularExpressions; using System.Collections.Generic; using mshtml; -using Microsoft.Win32; using System.Diagnostics; using System.Threading; using ExecutionContext = System.Management.Automation.ExecutionContext; @@ -20,7 +18,7 @@ namespace Microsoft.PowerShell.Commands /// /// Response object for html content /// - public class HtmlWebResponseObject : WebResponseObject, IDisposable + public partial class HtmlWebResponseObject : WebResponseObject, IDisposable { #region Properties @@ -223,37 +221,7 @@ namespace Microsoft.PowerShell.Commands } #endregion Properties - - #region Constructors - - /// - /// Constructor for HtmlWebResponseObject - /// - /// - /// - internal HtmlWebResponseObject(WebResponse response, ExecutionContext executionContext) - : this(response, null, executionContext) { } - - /// - /// Constructor for HtmlWebResponseObject with memory stream - /// - /// - /// /// - internal HtmlWebResponseObject(WebResponse response, Stream contentStream, ExecutionContext executionContext) - : base(response, contentStream) - { - if (executionContext == null) - { - throw PSTraceSource.NewArgumentNullException("executionContext"); - } - - _executionContext = executionContext; - InitializeContent(); - InitializeRawContent(response); - } - - #endregion Constructors - + #region Private Fields private static Regex _tagRegex; @@ -263,16 +231,6 @@ namespace Microsoft.PowerShell.Commands #endregion Private Fields #region Methods - - private void InitializeRawContent(WebResponse baseResponse) - { - StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); - if (null != Content) - { - raw.Append(Content); - } - this.RawContent = raw.ToString(); - } // The "onreadystatechange" event handler private void ReadyStateChanged(IHTMLEventObj obj) @@ -550,3 +508,4 @@ namespace Microsoft.PowerShell.Commands } } } +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs new file mode 100644 index 0000000000..899a9a54a7 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs @@ -0,0 +1,281 @@ +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Management.Automation; +using System.IO; +using System.Xml; + +namespace Microsoft.PowerShell.Commands +{ + public partial class InvokeRestMethodCommand + { + #region Parameters + + /// + /// gets or sets the parameter Method + /// + [Parameter] + public override WebRequestMethod Method + { + get { return base.Method; } + set { base.Method = value; } + } + + #endregion Parameters + + #region Helper Methods + + private bool TryProcessFeedStream(BufferingStreamReader responseStream) + { + bool isRssOrFeed = false; + + try + { + XmlReaderSettings readerSettings = GetSecureXmlReaderSettings(); + XmlReader reader = XmlReader.Create(responseStream, readerSettings); + + // See if the reader contained an "RSS" or "Feed" in the first 10 elements (RSS and Feed are normally 2 or 3) + int readCount = 0; + while ((readCount < 10) && reader.Read()) + { + if (String.Equals("rss", reader.Name, StringComparison.OrdinalIgnoreCase) || + String.Equals("feed", reader.Name, StringComparison.OrdinalIgnoreCase)) + { + isRssOrFeed = true; + break; + } + + readCount++; + } + + if (isRssOrFeed) + { + XmlDocument workingDocument = new XmlDocument(); + // performing a Read() here to avoid rrechecking + // "rss" or "feed" itmes + reader.Read(); + while (!reader.EOF) + { + // If node is Element and it's the 'Item' or 'Entry' node, emit that node. + if ((reader.NodeType == XmlNodeType.Element) && + (string.Equals("Item", reader.Name, StringComparison.OrdinalIgnoreCase) || + string.Equals("Entry", reader.Name, StringComparison.OrdinalIgnoreCase)) + ) + { + // this one will do reader.Read() internally + XmlNode result = workingDocument.ReadNode(reader); + WriteObject(result); + } + else + { + reader.Read(); + } + } + } + } + catch (XmlException) { } + finally + { + responseStream.Seek(0, SeekOrigin.Begin); + } + + return isRssOrFeed; + } + + // Mostly cribbed from Serialization.cs#GetXmlReaderSettingsForCliXml() + private XmlReaderSettings GetSecureXmlReaderSettings() + { + XmlReaderSettings xrs = new XmlReaderSettings(); + + xrs.CheckCharacters = false; + xrs.CloseInput = false; + + //The XML data needs to be in conformance to the rules for a well-formed XML 1.0 document. + xrs.IgnoreProcessingInstructions = true; + xrs.MaxCharactersFromEntities = 1024; + xrs.DtdProcessing = DtdProcessing.Ignore; + + return xrs; + } + + private bool TryConvertToXml(string xml, out object doc, ref Exception exRef) + { + try + { + XmlReaderSettings settings = GetSecureXmlReaderSettings(); + XmlReader xmlReader = XmlReader.Create(new StringReader(xml), settings); + + var xmlDoc = new XmlDocument(); + xmlDoc.PreserveWhitespace = true; + xmlDoc.Load(xmlReader); + + doc = xmlDoc; + } + catch (XmlException ex) + { + exRef = ex; + doc = null; + } + return (null != doc); + } + + private bool TryConvertToJson(string json, out object obj, ref Exception exRef) + { + try + { + ErrorRecord error; + obj = JsonObject.ConvertFromJson(json, out error); + + if (error != null) + { + exRef = error.Exception; + obj = null; + } + } + catch (ArgumentException ex) + { + exRef = ex; + obj = null; + } + catch (InvalidOperationException ex) + { + exRef = ex; + obj = null; + } + return (null != obj); + } + + #endregion + + /// + /// enum for rest return type. + /// + public enum RestReturnType + { + /// + /// Return type not defined in reponse, + /// best effort detect + /// + Detect, + + /// + /// Json return type + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + Json, + + /// + /// Xml return type + /// + Xml, + } + + internal class BufferingStreamReader : Stream + { + internal BufferingStreamReader(Stream baseStream) + { + this.baseStream = baseStream; + streamBuffer = new MemoryStream(); + this.length = long.MaxValue; + copyBuffer = new byte[4096]; + } + + private Stream baseStream; + private MemoryStream streamBuffer; + private byte[] copyBuffer; + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return true; } + } + + public override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + streamBuffer.SetLength(0); + } + + public override long Length + { + get { return this.length; } + } + private long length; + + public override long Position + { + get { return streamBuffer.Position; } + set { streamBuffer.Position = value; } + } + + public override int Read(byte[] buffer, int offset, int count) + { + long previousPosition = Position; + bool consumedStream = false; + int totalCount = count; + while ((!consumedStream) && + ((Position + totalCount) > streamBuffer.Length)) + { + // If we don't have enough data to fill this from memory, cache more. + // We try to read 4096 bytes from base stream every time, so at most we + // may cache 4095 bytes more than what is required by the Read operation. + int bytesRead = baseStream.Read(copyBuffer, 0, copyBuffer.Length); + + if (streamBuffer.Position < streamBuffer.Length) + { + // Win8: 651902 no need to -1 here as Position refers to the place + // where we can start writing from. + streamBuffer.Position = streamBuffer.Length; + } + + streamBuffer.Write(copyBuffer, 0, bytesRead); + + totalCount -= bytesRead; + if (bytesRead < copyBuffer.Length) + { + consumedStream = true; + } + } + + // Reset our backing store to its official position, as reading + // for the CopyTo updates the position. + streamBuffer.Seek(previousPosition, SeekOrigin.Begin); + + // Read from the backing store into the requested buffer. + int read = streamBuffer.Read(buffer, offset, count); + + if (read < count) + { + SetLength(Position); + } + + return read; + } + + public override long Seek(long offset, SeekOrigin origin) + { + return streamBuffer.Seek(offset, origin); + } + + public override void SetLength(long value) + { + this.length = value; + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs new file mode 100644 index 0000000000..6b070db23f --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs @@ -0,0 +1,625 @@ +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Collections.ObjectModel; +using System.Management.Automation; +using System.Net; +using System.IO; +using System.Text; +using System.Collections; +using System.Globalization; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +#if !CORECLR +using mshtml; +#endif +using Microsoft.Win32; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Base class for Invoke-RestMethod and Invoke-WebRequest commands. + /// + public abstract partial class WebRequestPSCmdlet : PSCmdlet + { + #region Virtual Properties + + #region URI + + /// + /// gets or sets the parameter UseBasicParsing + /// + [Parameter] + public virtual SwitchParameter UseBasicParsing { get; set; } + + /// + /// gets or sets the Uri property + /// + [Parameter(Position = 0, Mandatory = true)] + [ValidateNotNullOrEmpty] + public virtual Uri Uri { get; set; } + + #endregion + + #region Session + /// + /// gets or sets the Session property + /// + [Parameter] + public virtual WebRequestSession WebSession { get; set; } + + /// + /// gets or sets the SessionVariable property + /// + [Parameter] + [Alias("SV")] + public virtual string SessionVariable { get; set; } + + #endregion + + #region Authorization and Credentials + + /// + /// gets or sets the Credential property + /// + [Parameter] + [Credential] + public virtual PSCredential Credential { get; set; } + + /// + /// gets or sets the UseDefaultCredentials property + /// + [Parameter] + public virtual SwitchParameter UseDefaultCredentials { get; set; } + + /// + /// gets or sets the CertificateThumbprint property + /// + [Parameter] + [ValidateNotNullOrEmpty] + public virtual string CertificateThumbprint { get; set; } + + /// + /// gets or sets the Certificate property + /// + [Parameter] + [ValidateNotNull] + public virtual X509Certificate Certificate { get; set; } + + #endregion + + #region Headers + + /// + /// gets or sets the UserAgent property + /// + [Parameter] + public virtual string UserAgent { get; set; } + + /// + /// gets or sets the DisableKeepAlive property + /// + [Parameter] + public virtual SwitchParameter DisableKeepAlive { get; set; } + + /// + /// gets or sets the TimeOut property + /// + [Parameter] + [ValidateRange(0, Int32.MaxValue)] + public virtual int TimeoutSec { get; set; } + + /// + /// gets or sets the Headers property + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + [Parameter] + public virtual IDictionary Headers { get; set; } + + #endregion + + #region Redirect + + /// + /// gets or sets the RedirectMax property + /// + [Parameter] + [ValidateRange(0, Int32.MaxValue)] + public virtual int MaximumRedirection + { + get { return maximumRedirection; } + set { maximumRedirection = value; } + } + private int maximumRedirection = -1; + + #endregion + + #region Method + + /// + /// gets or sets the Method property + /// + [Parameter] + public virtual WebRequestMethod Method + { + get { return method; } + set { method = value; } + } + private WebRequestMethod method = WebRequestMethod.Default; + + #endregion + + #region Proxy + + /// + /// gets or sets the Proxy property + /// + [Parameter] + public virtual Uri Proxy { get; set; } + + /// + /// gets or sets the ProxyCredential property + /// + [Parameter] + [Credential] + public virtual PSCredential ProxyCredential { get; set; } + + /// + /// gets or sets the ProxyUseDefaultCredentials property + /// + [Parameter] + public virtual SwitchParameter ProxyUseDefaultCredentials { get; set; } + + #endregion + + #region Input + + /// + /// gets or sets the Body property + /// + [Parameter(ValueFromPipeline = true)] + public virtual object Body { get; set; } + + /// + /// gets or sets the ContentType property + /// + [Parameter] + public virtual string ContentType { get; set; } + + /// + /// gets or sets the TransferEncoding property + /// + [Parameter] + [ValidateSet("chunked", "compress", "deflate", "gzip", "identity", IgnoreCase = true)] + public virtual string TransferEncoding { get; set; } + + /// + /// gets or sets the InFile property + /// + [Parameter] + public virtual string InFile { get; set; } + + /// + /// keep the original file path after the resolved provider path is + /// assigned to InFile + /// + private string _originalFilePath; + + #endregion + + #region Output + + /// + /// gets or sets the OutFile property + /// + [Parameter] + public virtual string OutFile { get; set; } + + /// + /// gets or sets the PassThrough property + /// + [Parameter] + public virtual SwitchParameter PassThru { get; set; } + + #endregion + + #endregion Virtual Properties + + #region Virtual Methods + + internal virtual void ValidateParameters() + { + // sessions + if ((null != WebSession) && (null != SessionVariable)) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.SessionConflict, + "WebCmdletSessionConflictException"); + ThrowTerminatingError(error); + } + + // credentials + if (UseDefaultCredentials && (null != Credential)) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.CredentialConflict, + "WebCmdletCredentialConflictException"); + ThrowTerminatingError(error); + } + + // Proxy server + if (ProxyUseDefaultCredentials && (null != ProxyCredential)) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.ProxyCredentialConflict, + "WebCmdletProxyCredentialConflictException"); + ThrowTerminatingError(error); + } + else if ((null == Proxy) && ((null != ProxyCredential) || ProxyUseDefaultCredentials)) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.ProxyUriNotSupplied, + "WebCmdletProxyUriNotSuppliedException"); + ThrowTerminatingError(error); + } + + // request body content + if ((null != Body) && (null != InFile)) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.BodyConflict, + "WebCmdletBodyConflictException"); + ThrowTerminatingError(error); + } + + // validate InFile path + if (InFile != null) + { + ProviderInfo provider = null; + ErrorRecord errorRecord = null; + + try + { + Collection providerPaths = GetResolvedProviderPathFromPSPath(InFile, out provider); + + if (!provider.Name.Equals(FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) + { + errorRecord = GetValidationError(WebCmdletStrings.NotFilesystemPath, + "WebCmdletInFileNotFilesystemPathException", InFile); + } + else + { + if (providerPaths.Count > 1) + { + errorRecord = GetValidationError(WebCmdletStrings.MultiplePathsResolved, + "WebCmdletInFileMultiplePathsResolvedException", InFile); + } + else if (providerPaths.Count == 0) + { + errorRecord = GetValidationError(WebCmdletStrings.NoPathResolved, + "WebCmdletInFileNoPathResolvedException", InFile); + } + else + { + if (Directory.Exists(providerPaths[0])) + { + errorRecord = GetValidationError(WebCmdletStrings.DirecotryPathSpecified, + "WebCmdletInFileNotFilePathException", InFile); + } + _originalFilePath = InFile; + InFile = providerPaths[0]; + } + } + } + catch (ItemNotFoundException pathNotFound) + { + errorRecord = new ErrorRecord(pathNotFound.ErrorRecord, pathNotFound); + } + catch (ProviderNotFoundException providerNotFound) + { + errorRecord = new ErrorRecord(providerNotFound.ErrorRecord, providerNotFound); + } + catch (System.Management.Automation.DriveNotFoundException driveNotFound) + { + errorRecord = new ErrorRecord(driveNotFound.ErrorRecord, driveNotFound); + } + + if (errorRecord != null) + { + ThrowTerminatingError(errorRecord); + } + } + + // output ?? + if (PassThru && (OutFile == null)) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.OutFileMissing, + "WebCmdletOutFileMissingException"); + ThrowTerminatingError(error); + } + } + + internal virtual void PrepareSession() + { + // make sure we have a valid WebRequestSession object to work with + if (null == WebSession) + { + WebSession = new WebRequestSession(); + } + + if (null != SessionVariable) + { + // save the session back to the PS environment if requested + PSVariableIntrinsics vi = SessionState.PSVariable; + vi.Set(SessionVariable, WebSession); + } + + // + // handle credentials + // + if (null != Credential) + { + // get the relevant NetworkCredential + NetworkCredential netCred = Credential.GetNetworkCredential(); + WebSession.Credentials = netCred; + + // supplying a credential overrides the UseDefaultCredentials setting + WebSession.UseDefaultCredentials = false; + } + else if (UseDefaultCredentials) + { + WebSession.UseDefaultCredentials = true; + } + + + if (null != CertificateThumbprint) + { + X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); + X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates; + X509Certificate2Collection tbCollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByThumbprint, CertificateThumbprint, false); + if (tbCollection.Count == 0) + { + CryptographicException ex = new CryptographicException(WebCmdletStrings.ThumbprintNotFound); + throw ex; + } + foreach (X509Certificate2 tbCert in tbCollection) + { + X509Certificate certificate = (X509Certificate)tbCert; + WebSession.AddCertificate(certificate); + } + } + + if (null != Certificate) + { + WebSession.AddCertificate(Certificate); + } + + // + // handle the user agent + // + if (null != UserAgent) + { + // store the UserAgent string + WebSession.UserAgent = UserAgent; + } + + if (null != Proxy) + { + WebProxy webProxy = new WebProxy(Proxy); + webProxy.BypassProxyOnLocal = false; + if (null != ProxyCredential) + { + webProxy.Credentials = ProxyCredential.GetNetworkCredential(); + } + else if (ProxyUseDefaultCredentials) + { + // If both ProxyCredential and ProxyUseDefaultCredentials are passed, + // UseDefaultCredentials will overwrite the supplied credentials. + webProxy.UseDefaultCredentials = true; + } + WebSession.Proxy = webProxy; + } + + if (-1 < MaximumRedirection) + { + WebSession.MaximumRedirection = MaximumRedirection; + } + + // store the other supplied headers + if (null != Headers) + { + foreach (string key in Headers.Keys) + { + // add the header value (or overwrite it if already present) + WebSession.Headers[key] = Headers[key].ToString(); + } + } + } + + #endregion Virtual Methods + + #region Helper Properties + + internal string QualifiedOutFile + { + get { return (QualifyFilePath(OutFile)); } + } + + internal bool ShouldSaveToOutFile + { + get { return (!string.IsNullOrEmpty(OutFile)); } + } + + internal bool ShouldWriteToPipeline + { + get { return (!ShouldSaveToOutFile || PassThru); } + } + + #endregion Helper Properties + + #region Helper Methods + + /// + /// Verifies that Internet Explorer is available, and that its first-run + /// configuration is complete. + /// + /// True if we should try to access IE's COM object. Not + /// needed if an HtmlDocument will be created shortly. + protected bool VerifyInternetExplorerAvailable(bool checkComObject) + { + // TODO: Remove this code once the dependecy on mshtml has been resolved. +#if CORECLR + return false; +#else + bool isInternetExplorerConfigurationComplete = false; + // Check for IE for both PS Full and PS Core on windows. + // The registry key DisableFirstRunCustomize can exits at one of the following path. + // IE uses the same decending orider (as mentioned) to check for the presence of this key. + // If the value of DisableFirstRunCustomize key is set to greater than zero then Run first + // is disabled. + string[] disableFirstRunCustomizePaths = new string[] { + @"HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Internet Explorer\Main", + @"HKEY_CURRENT_USER\Software\Policies\Microsoft\Internet Explorer\Main", + @"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main", + @"HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Main" }; + + foreach (string currentRegPath in disableFirstRunCustomizePaths) + { + object val = Registry.GetValue(currentRegPath, "DisableFirstRunCustomize", string.Empty); + if (val != null && !string.Empty.Equals(val) && Convert.ToInt32(val, CultureInfo.InvariantCulture) > 0) + { + isInternetExplorerConfigurationComplete = true; + break; + } + } + + if (!isInternetExplorerConfigurationComplete) + { + // Verify that if IE is installed, it has been through the RunOnce check. + // Otherwise, the call will hang waiting for users to go through First Run + // personalization. + using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Internet Explorer\\Main")) + { + if (key != null) + { + foreach (string setting in key.GetValueNames()) + { + if (setting.IndexOf("RunOnce", StringComparison.OrdinalIgnoreCase) > -1) + { + isInternetExplorerConfigurationComplete = true; + break; + } + } + } + } + } + + if (isInternetExplorerConfigurationComplete && checkComObject) + { + try + { + IHTMLDocument2 ieCheck = (IHTMLDocument2)new HTMLDocument(); + System.Runtime.InteropServices.Marshal.ReleaseComObject(ieCheck); + } + catch (System.Runtime.InteropServices.COMException) + { + isInternetExplorerConfigurationComplete = false; + } + } + + // Throw exception in PS Full only + if (!isInternetExplorerConfigurationComplete) + throw new NotSupportedException(WebCmdletStrings.IEDomNotSupported); + return isInternetExplorerConfigurationComplete; +#endif + } + + private Uri PrepareUri(Uri uri) + { + uri = CheckProtocol(uri); + + // before creating the web request, + // preprocess Body if content is a dictionary and method is GET (set as query) + IDictionary bodyAsDictionary; + LanguagePrimitives.TryConvertTo(Body, out bodyAsDictionary); + if ((null != bodyAsDictionary) + && (Method == WebRequestMethod.Default || Method == WebRequestMethod.Get)) + { + UriBuilder uriBuilder = new UriBuilder(uri); + if (uriBuilder.Query != null && uriBuilder.Query.Length > 1) + { + uriBuilder.Query = uriBuilder.Query.Substring(1) + "&" + FormatDictionary(bodyAsDictionary); + } + else + { + uriBuilder.Query = FormatDictionary(bodyAsDictionary); + } + uri = uriBuilder.Uri; + // set body to null to prevent later FillRequestStream + Body = null; + } + + return uri; + } + + private Uri CheckProtocol(Uri uri) + { + if (null == uri) { throw new ArgumentNullException("uri"); } + + if (!uri.IsAbsoluteUri) + { + uri = new Uri("http://" + uri.OriginalString); + } + return (uri); + } + + private string QualifyFilePath(string path) + { + string resolvedFilePath = PathUtils.ResolveFilePath(path, this, false); + return resolvedFilePath; + } + + private string FormatDictionary(IDictionary content) + { + if (content == null) + throw new ArgumentNullException("content"); + + StringBuilder bodyBuilder = new StringBuilder(); + foreach (string key in content.Keys) + { + if (0 < bodyBuilder.Length) + { + bodyBuilder.Append("&"); + } + + object value = content[key]; + + // URLEncode the key and value + string encodedKey = WebUtility.UrlEncode(key); + string encodedValue = String.Empty; + if (null != value) + { + encodedValue = WebUtility.UrlEncode(value.ToString()); + } + + bodyBuilder.AppendFormat("{0}={1}", encodedKey, encodedValue); + } + return bodyBuilder.ToString(); + } + + private ErrorRecord GetValidationError(string msg, string errorId) + { + var ex = new ValidationMetadataException(msg); + var error = new ErrorRecord(ex, errorId, ErrorCategory.InvalidArgument, this); + return (error); + } + + private ErrorRecord GetValidationError(string msg, string errorId, params object[] args) + { + msg = string.Format(CultureInfo.InvariantCulture, msg, args); + var ex = new ValidationMetadataException(msg); + var error = new ErrorRecord(ex, errorId, ErrorCategory.InvalidArgument, this); + return (error); + } + + #endregion Helper Methods + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs new file mode 100644 index 0000000000..c36acd32b0 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs @@ -0,0 +1,99 @@ +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// WebResponseObject + /// + public partial class WebResponseObject + { + #region Properties + + /// + /// gets or protected sets the Content property + /// + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public byte[] Content { get; protected set; } + + /// + /// gets the StatusCode property + /// + public int StatusCode + { + get { return (WebResponseHelper.GetStatusCode(BaseResponse)); } + } + + /// + /// gets the StatusDescription property + /// + public string StatusDescription + { + get { return (WebResponseHelper.GetStatusDescription(BaseResponse)); } + } + + private MemoryStream _rawContentStream; + /// + /// gets the RawContentStream property + /// + public MemoryStream RawContentStream + { + get { return (_rawContentStream); } + } + + /// + /// gets the RawContentLength property + /// + public long RawContentLength + { + get { return (null == RawContentStream ? -1 : RawContentStream.Length); } + } + + /// + /// gets or protected sets the RawContent property + /// + public string RawContent { get; protected set; } + + #endregion Properties + + #region Methods + + /// + /// Reads the response content from the web response. + /// + private void InitializeContent() + { + this.Content = this.RawContentStream.ToArray(); + } + + private bool IsPrintable(char c) + { + return (Char.IsLetterOrDigit(c) || Char.IsPunctuation(c) || Char.IsSeparator(c) || Char.IsSymbol(c) || Char.IsWhiteSpace(c)); + } + + /// + /// Returns the string representation of this web response. + /// + /// The string representation of this web response. + public sealed override string ToString() + { + char[] stringContent = System.Text.Encoding.ASCII.GetChars(Content); + for (int counter = 0; counter < stringContent.Length; counter++) + { + if (!IsPrintable(stringContent[counter])) + { + stringContent[counter] = '.'; + } + } + + return new string(stringContent); + } + + #endregion Methods + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/BasicHtmlWebResponseObject.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/BasicHtmlWebResponseObject.CoreClr.cs new file mode 100644 index 0000000000..b1f09a5b7a --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/BasicHtmlWebResponseObject.CoreClr.cs @@ -0,0 +1,54 @@ +#if CORECLR + +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System.Net.Http; +using System.IO; +using System.Text; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Response object for html content without DOM parsing + /// + public partial class BasicHtmlWebResponseObject : WebResponseObject + { + #region Constructors + + /// + /// Constructor for HtmlWebResponseObject + /// + /// + public BasicHtmlWebResponseObject(HttpResponseMessage response) + : this(response, null) { } + + /// + /// Constructor for HtmlWebResponseObject with memory stream + /// + /// + /// + public BasicHtmlWebResponseObject(HttpResponseMessage response, Stream contentStream) + : base(response, contentStream) + { + EnsureHtmlParser(); + InitializeContent(); + InitializeRawContent(response); + } + + #endregion Constructors + + #region Methods + + private void InitializeRawContent(HttpResponseMessage baseResponse) + { + StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); + raw.Append(Content); + this.RawContent = raw.ToString(); + } + + #endregion Methods + } +} +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs new file mode 100644 index 0000000000..5bd568a1c7 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs @@ -0,0 +1,50 @@ +#if CORECLR + +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System.Text; +using System.Linq; +using System.Net.Http; + +namespace Microsoft.PowerShell.Commands +{ + internal static partial class ContentHelper + { + internal static Encoding GetEncoding(HttpResponseMessage response) + { + string charSet = response.Content.Headers.ContentType.CharSet; + return GetEncodingOrDefault(charSet); + } + + internal static string GetContentType(HttpResponseMessage response) + { + return response.Content.Headers.ContentType.MediaType; + } + + internal static StringBuilder GetRawContentHeader(HttpResponseMessage response) + { + StringBuilder raw = new StringBuilder(); + + string protocol = WebResponseHelper.GetProtocol(response); + if (!string.IsNullOrEmpty(protocol)) + { + int statusCode = WebResponseHelper.GetStatusCode(response); + string statusDescription = WebResponseHelper.GetStatusDescription(response); + raw.AppendFormat("{0} {1} {2}", protocol, statusCode, statusDescription); + raw.AppendLine(); + } + + foreach (var entry in response.Headers) + { + raw.AppendFormat("{0}: {1}", entry.Key, entry.Value.FirstOrDefault()); + raw.AppendLine(); + } + + raw.AppendLine(); + return raw; + } + } +} +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs new file mode 100644 index 0000000000..2b6fc8f976 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs @@ -0,0 +1,73 @@ +#if CORECLR + +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Management.Automation; +using System.Net.Http; +using System.IO; +using System.Text; +using ExecutionContext = System.Management.Automation.ExecutionContext; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Response object for html content + /// + public partial class HtmlWebResponseObject : WebResponseObject, IDisposable + { + #region Constructors + + /// + /// Constructor for HtmlWebResponseObject + /// + /// + /// + internal HtmlWebResponseObject(HttpResponseMessage response, ExecutionContext executionContext) + : this(response, null, executionContext) { } + + /// + /// Constructor for HtmlWebResponseObject with memory stream + /// + /// + /// /// + internal HtmlWebResponseObject(HttpResponseMessage response, Stream contentStream, ExecutionContext executionContext) + : base(response, contentStream) + { + if (executionContext == null) + { + throw PSTraceSource.NewArgumentNullException("executionContext"); + } + + _executionContext = executionContext; + InitializeContent(); + InitializeRawContent(response); + } + + #endregion Constructors + + #region Methods + + private void InitializeRawContent(HttpResponseMessage baseResponse) + { + StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); + if (null != Content) + { + raw.Append(Content); + } + this.RawContent = raw.ToString(); + } + + /// + /// Dispose the the instance of the class. + /// + public void Dispose() + { + GC.SuppressFinalize(this); + } + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HttpKnownHeaderNames.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HttpKnownHeaderNames.cs new file mode 100644 index 0000000000..754e6f47be --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HttpKnownHeaderNames.cs @@ -0,0 +1,110 @@ +#if CORECLR + +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Collections.Generic; + +namespace Microsoft.PowerShell.Commands +{ + internal static partial class HttpKnownHeaderNames + { + #region Known_HTTP_Header_Names + + // Known HTTP Header Names. + // List comes from corefx/System/Net/HttpKnownHeaderNames.cs + public const string Accept = "Accept"; + public const string AcceptCharset = "Accept-Charset"; + public const string AcceptEncoding = "Accept-Encoding"; + public const string AcceptLanguage = "Accept-Language"; + public const string AcceptRanges = "Accept-Ranges"; + public const string Age = "Age"; + public const string Allow = "Allow"; + public const string Authorization = "Authorization"; + public const string CacheControl = "Cache-Control"; + public const string Connection = "Connection"; + public const string ContentDisposition = "Content-Disposition"; + public const string ContentEncoding = "Content-Encoding"; + public const string ContentLanguage = "Content-Language"; + public const string ContentLength = "Content-Length"; + public const string ContentLocation = "Content-Location"; + public const string ContentMD5 = "Content-MD5"; + public const string ContentRange = "Content-Range"; + public const string ContentType = "Content-Type"; + public const string Cookie = "Cookie"; + public const string Cookie2 = "Cookie2"; + public const string Date = "Date"; + public const string ETag = "ETag"; + public const string Expect = "Expect"; + public const string Expires = "Expires"; + public const string From = "From"; + public const string Host = "Host"; + public const string IfMatch = "If-Match"; + public const string IfModifiedSince = "If-Modified-Since"; + public const string IfNoneMatch = "If-None-Match"; + public const string IfRange = "If-Range"; + public const string IfUnmodifiedSince = "If-Unmodified-Since"; + public const string KeepAlive = "Keep-Alive"; + public const string LastModified = "Last-Modified"; + public const string Location = "Location"; + public const string MaxForwards = "Max-Forwards"; + public const string Origin = "Origin"; + public const string P3P = "P3P"; + public const string Pragma = "Pragma"; + public const string ProxyAuthenticate = "Proxy-Authenticate"; + public const string ProxyAuthorization = "Proxy-Authorization"; + public const string ProxyConnection = "Proxy-Connection"; + public const string Range = "Range"; + public const string Referer = "Referer"; // NB: The spelling-mistake "Referer" for "Referrer" must be matched. + public const string RetryAfter = "Retry-After"; + public const string SecWebSocketAccept = "Sec-WebSocket-Accept"; + public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions"; + public const string SecWebSocketKey = "Sec-WebSocket-Key"; + public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol"; + public const string SecWebSocketVersion = "Sec-WebSocket-Version"; + public const string Server = "Server"; + public const string SetCookie = "Set-Cookie"; + public const string SetCookie2 = "Set-Cookie2"; + public const string TE = "TE"; + public const string Trailer = "Trailer"; + public const string TransferEncoding = "Transfer-Encoding"; + public const string Upgrade = "Upgrade"; + public const string UserAgent = "User-Agent"; + public const string Vary = "Vary"; + public const string Via = "Via"; + public const string WWWAuthenticate = "WWW-Authenticate"; + public const string Warning = "Warning"; + public const string XAspNetVersion = "X-AspNet-Version"; + public const string XPoweredBy = "X-Powered-By"; + + #endregion Known_HTTP_Header_Names + + private static HashSet _contentHeaderSet = null; + internal static HashSet ContentHeaders + { + get { + if (_contentHeaderSet == null) + { + _contentHeaderSet = new HashSet(StringComparer.OrdinalIgnoreCase); + + _contentHeaderSet.Add(HttpKnownHeaderNames.Allow); + _contentHeaderSet.Add(HttpKnownHeaderNames.ContentDisposition); + _contentHeaderSet.Add(HttpKnownHeaderNames.ContentEncoding); + _contentHeaderSet.Add(HttpKnownHeaderNames.ContentLanguage); + _contentHeaderSet.Add(HttpKnownHeaderNames.ContentLength); + _contentHeaderSet.Add(HttpKnownHeaderNames.ContentLocation); + _contentHeaderSet.Add(HttpKnownHeaderNames.ContentMD5); + _contentHeaderSet.Add(HttpKnownHeaderNames.ContentRange); + _contentHeaderSet.Add(HttpKnownHeaderNames.ContentType); + _contentHeaderSet.Add(HttpKnownHeaderNames.Expires); + _contentHeaderSet.Add(HttpKnownHeaderNames.LastModified); + } + + return _contentHeaderSet; + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeRestMethodCommand.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeRestMethodCommand.CoreClr.cs new file mode 100644 index 0000000000..679764f792 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeRestMethodCommand.CoreClr.cs @@ -0,0 +1,115 @@ +#if CORECLR + +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Management.Automation; +using System.Net.Http; +using System.Text; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// The Invoke-RestMethod command + /// This command makes an HTTP or HTTPS request to a web service, + /// and returns the response in an appropriate way. + /// Intended to work against the wide spectrum of "RESTful" web services + /// currently deployed across the web. + /// + [Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=217034")] + public partial class InvokeRestMethodCommand : WebRequestPSCmdlet + { + #region Virtual Method Overrides + + /// + /// Process the web reponse and output corresponding objects. + /// + /// + internal override void ProcessResponse(HttpResponseMessage response) + { + if (null == response) { throw new ArgumentNullException("response"); } + + using (BufferingStreamReader responseStream = new BufferingStreamReader(StreamHelper.GetResponseStream(response))) + { + if (ShouldWriteToPipeline) + { + // First see if it is an RSS / ATOM feed, in which case we can + // stream it - unless the user has overriden it with a return type of "XML" + if (TryProcessFeedStream(responseStream)) + { + // Do nothing, content has been processed. + } + else + { + // determine the response type + RestReturnType returnType = CheckReturnType(response); + // get the response encoding + Encoding encoding = ContentHelper.GetEncoding(response); + + object obj = null; + Exception ex = null; + + string str = StreamHelper.DecodeStream(responseStream, encoding); + bool convertSuccess = false; + + // On CoreCLR, we need to explicity load Json.NET + JsonObject.ImportJsonDotNetModule(this); + if (returnType == RestReturnType.Json) + { + convertSuccess = TryConvertToJson(str, out obj, ref ex) || TryConvertToXml(str, out obj, ref ex); + } + // default to try xml first since it's more common + else + { + convertSuccess = TryConvertToXml(str, out obj, ref ex) || TryConvertToJson(str, out obj, ref ex); + } + + if (!convertSuccess) + { + // fallback to string + obj = str; + } + + WriteObject(obj); + } + } + + if (ShouldSaveToOutFile) + { + StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this); + } + } + } + + #endregion Virtual Method Overrides + + #region Helper Methods + + private RestReturnType CheckReturnType(HttpResponseMessage response) + { + if (null == response) { throw new ArgumentNullException("response"); } + + RestReturnType rt = RestReturnType.Detect; + string contentType = ContentHelper.GetContentType(response); + if (string.IsNullOrEmpty(contentType)) + { + rt = RestReturnType.Detect; + } + else if (ContentHelper.IsJson(contentType)) + { + rt = RestReturnType.Json; + } + else if (ContentHelper.IsXml(contentType)) + { + rt = RestReturnType.Xml; + } + + return (rt); + } + + #endregion Helper Methods + } +} +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs new file mode 100644 index 0000000000..fae5afe95d --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs @@ -0,0 +1,67 @@ +#if CORECLR + +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Management.Automation; +using System.Net.Http; +using System.IO; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// The Invoke-RestMethod command + /// This command makes an HTTP or HTTPS request to a web server and returns the results. + /// + [Cmdlet(VerbsLifecycle.Invoke, "WebRequest", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=217035")] + public class InvokeWebRequestCommand : WebRequestPSCmdlet + { + #region Virtual Method Overrides + + /// + /// Process the web reponse and output corresponding objects. + /// + /// + internal override void ProcessResponse(HttpResponseMessage response) + { + if (null == response) { throw new ArgumentNullException("response"); } + + // check for Server Core, throws exception if -UseBasicParsing is not used + if (ShouldWriteToPipeline && !UseBasicParsing) + { + // IE is not available in PS Linux, and may not available in PS Core depending on + // where it's running (desktop/nano/iot). + // For PS Linux and PS Core, if IE is not available, we always use basic parsing. + if (!VerifyInternetExplorerAvailable(true)) + { + UseBasicParsing = true; + } + } + + Stream responseStream = StreamHelper.GetResponseStream(response); + if (ShouldWriteToPipeline) + { + // creating a MemoryStream wrapper to response stream here to support IsStopping. + responseStream = new WebResponseContentMemoryStream(responseStream, StreamHelper.ChunkSize, this); + WebResponseObject ro = WebResponseObjectFactory.GetResponseObject(response, responseStream, this.Context, UseBasicParsing); + WriteObject(ro); + + // use the rawcontent stream from WebRepsonseObject for further + // processing of the stream. This is need because WebResponse's + // stream can be used only once. + responseStream = ro.RawContentStream; + responseStream.Seek(0, SeekOrigin.Begin); + } + + if (ShouldSaveToOutFile) + { + StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this); + } + } + + #endregion Virtual Method Overrides + } +} +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebProxy.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebProxy.cs new file mode 100644 index 0000000000..61393b5622 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebProxy.cs @@ -0,0 +1,70 @@ +#if CORECLR + +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Net; + +namespace Microsoft.PowerShell.Commands +{ + internal class WebProxy : IWebProxy + { + private ICredentials _credentials; + private Uri _proxyAddress; + + internal WebProxy (Uri address) + { + if (address == null) + { + throw new ArgumentNullException("address"); + } + + _proxyAddress = address; + } + + public ICredentials Credentials + { + get { return _credentials; } + set { _credentials = value; } + } + + internal bool BypassProxyOnLocal + { + get; set; + } + + internal bool UseDefaultCredentials + { + get { + return _credentials == CredentialCache.DefaultCredentials; + } + set { + _credentials = value ? CredentialCache.DefaultCredentials : null; + } + } + + public Uri GetProxy(Uri destination) + { + if (destination == null) + { + throw new ArgumentNullException("destination"); + } + + if (destination.IsLoopback) + { + return destination; + } + + return _proxyAddress; + + } + + public bool IsBypassed(Uri host) + { + return host.IsLoopback; + } + } +} +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs new file mode 100644 index 0000000000..a13f99406e --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs @@ -0,0 +1,541 @@ +#if CORECLR + +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Management.Automation; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.IO; +using System.Text; +using System.Collections; +using System.Globalization; +using System.Security.Cryptography; +using System.Threading; +using System.Xml; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Base class for Invoke-RestMethod and Invoke-WebRequest commands. + /// + public abstract partial class WebRequestPSCmdlet : PSCmdlet + { + #region Abstract Methods + + /// + /// Read the supplied WebResponse object and push the + /// resulting output into the pipeline. + /// + /// Instance of a WebResponse object to be processed + internal abstract void ProcessResponse(HttpResponseMessage response); + + #endregion Abstract Methods + + /// + /// Cancellation token source + /// + private CancellationTokenSource cancelToken = null; + + private HttpMethod GetHttpMethod(WebRequestMethod method) + { + switch (Method) + { + case WebRequestMethod.Default: + case WebRequestMethod.Get: + return HttpMethod.Get; + case WebRequestMethod.Head: + return HttpMethod.Head; + case WebRequestMethod.Post: + return HttpMethod.Post; + case WebRequestMethod.Put: + return HttpMethod.Put; + case WebRequestMethod.Delete: + return HttpMethod.Delete; + case WebRequestMethod.Trace: + return HttpMethod.Trace; + case WebRequestMethod.Options: + return HttpMethod.Options; + default: + // Merge and Patch + return new HttpMethod(Method.ToString().ToUpperInvariant()); + } + } + + #region Virtual Methods + + internal virtual HttpClient GetHttpClient() + { + // By default the HttpClientHandler will automatically decompress GZip and Deflate content + HttpClientHandler handler = new HttpClientHandler(); + handler.CookieContainer = WebSession.Cookies; + + // set the credentials used by this request + if (WebSession.UseDefaultCredentials) + { + // the UseDefaultCredentials flag overrides other supplied credentials + handler.UseDefaultCredentials = true; + } + else if (WebSession.Credentials != null) + { + handler.Credentials = WebSession.Credentials; + } + + if (WebSession.Proxy != null) + { + handler.Proxy = WebSession.Proxy; + } + + /* + TODO: HttpClientHandler will support client certificate in RTM + See https://github.com/dotnet/corefx/issues/7623 for more details. + if (null != WebSession.Certificates) + { + handler.ClientCertificates = WebSession.Certificates; + }*/ + + if (WebSession.MaximumRedirection > -1) + { + if (WebSession.MaximumRedirection == 0) + { + handler.AllowAutoRedirect = false; + } + else + { + handler.MaxAutomaticRedirections = WebSession.MaximumRedirection; + } + } + + HttpClient httpClient = new HttpClient(handler); + + // check timeout setting (in seconds instead of milliseconds as in HttpWebRequest) + if (TimeoutSec == 0) + { + // A zero timeout means infinite + httpClient.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite); + } + else if (TimeoutSec > 0) + { + httpClient.Timeout = new TimeSpan(0, 0, TimeoutSec); + } + + return httpClient; + } + + internal virtual HttpRequestMessage GetRequest(Uri uri) + { + Uri requestUri = PrepareUri(uri); + HttpMethod httpMethod = GetHttpMethod(Method); + + // create the base WebRequest object + var request = new HttpRequestMessage(httpMethod, requestUri); + + // pull in session data + if (WebSession.Headers.Count > 0) + { + WebSession.ContentHeaders.Clear(); + foreach (var entry in WebSession.Headers) + { + if (HttpKnownHeaderNames.ContentHeaders.Contains(entry.Key)) + { + WebSession.ContentHeaders.Add(entry.Key, entry.Value); + } + else + { + request.Headers.Add(entry.Key, entry.Value); + } + } + } + + // Set 'Transfer-Encoding: chunked' if 'Transfer-Encoding' is specified + if (WebSession.Headers.ContainsKey(HttpKnownHeaderNames.TransferEncoding)) + { + request.Headers.TransferEncodingChunked = true; + } + + // Set 'User-Agent' if WebSession.Headers doesn't already contain it + string userAgent = null; + if (WebSession.Headers.TryGetValue(HttpKnownHeaderNames.UserAgent, out userAgent)) + { + WebSession.UserAgent = userAgent; + } + else + { + request.Headers.Add(HttpKnownHeaderNames.UserAgent, WebSession.UserAgent); + } + + // Set 'Keep-Alive' to false. This means set the Connection to 'Close'. + if (DisableKeepAlive) + { + request.Headers.Add(HttpKnownHeaderNames.Connection, "Close"); + } + + // Set 'Transfer-Encoding' + if (TransferEncoding != null) + { + request.Headers.TransferEncodingChunked = true; + var headerValue = new TransferCodingHeaderValue(TransferEncoding); + if (!request.Headers.TransferEncoding.Contains(headerValue)) + { + request.Headers.TransferEncoding.Add(headerValue); + } + } + + // Some web sites (e.g. Twitter) will return exception on POST when Expect100 is sent + // Default behaviour is continue to send body content anyway after a short period + // Here it send the two part as a whole. + request.Headers.ExpectContinue = false; + + return (request); + } + + internal virtual void FillRequestStream(HttpRequestMessage request) + { + if (null == request) { throw new ArgumentNullException("request"); } + + // set the content type + if (ContentType != null) + { + WebSession.ContentHeaders[HttpKnownHeaderNames.ContentType] = ContentType; + //request + } + // ContentType == null + else if (Method == WebRequestMethod.Post) + { + // Win8:545310 Invoke-WebRequest does not properly set MIME type for POST + string contentType = null; + WebSession.ContentHeaders.TryGetValue(HttpKnownHeaderNames.ContentType, out contentType); + if (string.IsNullOrEmpty(contentType)) + { + WebSession.ContentHeaders[HttpKnownHeaderNames.ContentType] = "application/x-www-form-urlencoded"; + } + } + + // coerce body into a usable form + if (Body != null) + { + object content = Body; + + // make sure we're using the base object of the body, not the PSObject wrapper + PSObject psBody = Body as PSObject; + if (psBody != null) + { + content = psBody.BaseObject; + } + + /* TODO: This needs to be enable after the dependency on mshtml is resolved. + var html = content as HtmlWebResponseObject; + if (html != null) + { + // use the form if it's the only one present + if (html.Forms.Count == 1) + { + SetRequestContent(request, html.Forms[0].Fields); + } + } + else if (content is FormObject) + */ + + if (content is FormObject) + { + FormObject form = content as FormObject; + SetRequestContent(request, form.Fields); + } + else if (content is IDictionary && request.Method != HttpMethod.Get) + { + IDictionary dictionary = content as IDictionary; + SetRequestContent(request, dictionary); + } + else if (content is XmlNode) + { + XmlNode xmlNode = content as XmlNode; + SetRequestContent(request, xmlNode); + } + else if (content is Stream) + { + Stream stream = content as Stream; + SetRequestContent(request, stream); + } + else if (content is byte[]) + { + byte[] bytes = content as byte[]; + SetRequestContent(request, bytes); + } + else + { + SetRequestContent(request, + (string)LanguagePrimitives.ConvertTo(content, typeof(string), CultureInfo.InvariantCulture)); + } + } + else if (InFile != null) // copy InFile data + { + try + { + // open the input file + using (FileStream fs = new FileStream(InFile, FileMode.Open)) + { + SetRequestContent(request, fs); + } + } + catch (UnauthorizedAccessException) + { + string msg = string.Format(CultureInfo.InvariantCulture, WebCmdletStrings.AccessDenied, + _originalFilePath); + throw new UnauthorizedAccessException(msg); + } + } + + // Add the content headers + if (request.Content != null) + { + foreach (var entry in WebSession.ContentHeaders) + { + request.Content.Headers.Add(entry.Key, entry.Value); + } + } + } + + internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestMessage request) + { + if (client == null) { throw new ArgumentNullException("client"); } + if (request == null) { throw new ArgumentNullException("request"); } + + cancelToken = new CancellationTokenSource(); + return client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancelToken.Token).GetAwaiter().GetResult(); + } + + internal virtual void UpdateSession(HttpResponseMessage response) + { + if (response == null) { throw new ArgumentNullException("response"); } + } + + #endregion Virtual Methods + + #region Overrides + + /// + /// the main execution method for cmdlets derived from WebRequestPSCmdlet. + /// + protected override void ProcessRecord() + { + try + { + // Set cmdlet context for write progress + ValidateParameters(); + PrepareSession(); + HttpClient client = GetHttpClient(); + HttpRequestMessage request = GetRequest(Uri); + FillRequestStream(request); + + try + { + long requestContentLength = 0; + if (request.Content != null) + requestContentLength = request.Content.Headers.ContentLength.Value; + + string reqVerboseMsg = String.Format(CultureInfo.CurrentCulture, + "{0} {1} with {2}-byte payload", + request.Method, + request.RequestUri, + requestContentLength); + WriteVerbose(reqVerboseMsg); + + HttpResponseMessage response = GetResponse(client, request); + response.EnsureSuccessStatusCode(); + + string contentType = ContentHelper.GetContentType(response); + string respVerboseMsg = string.Format(CultureInfo.CurrentCulture, + "received {0}-byte response of content type {1}", + response.Content.Headers.ContentLength, + contentType); + WriteVerbose(respVerboseMsg); + ProcessResponse(response); + UpdateSession(response); + + // If we hit our maxium redirection count, generate an error. + // Errors with redirection counts of greater than 0 are handled automatically by .NET, but are + // impossible to detect programmatically when we hit this limit. By handling this ourselves + // (and still writing out the result), users can debug actual HTTP redirect problems. + if (WebSession.MaximumRedirection == 0) // Indicate "HttpClientHandler.AllowAutoRedirect == false" + { + if (response.StatusCode == HttpStatusCode.Found || + response.StatusCode == HttpStatusCode.Moved || + response.StatusCode == HttpStatusCode.MovedPermanently) + { + ErrorRecord er = new ErrorRecord(new InvalidOperationException(), "MaximumRedirectExceeded", ErrorCategory.InvalidOperation, request); + er.ErrorDetails = new ErrorDetails(WebCmdletStrings.MaximumRedirectionCountExceeded); + WriteError(er); + } + } + } + catch (HttpRequestException ex) + { + ErrorRecord er = new ErrorRecord(ex, "WebCmdletWebResponseException", ErrorCategory.InvalidOperation, request); + ThrowTerminatingError(er); + } + } + catch (CryptographicException ex) + { + ErrorRecord er = new ErrorRecord(ex, "WebCmdletCertificateException", ErrorCategory.SecurityError, null); + ThrowTerminatingError(er); + } + catch (NotSupportedException ex) + { + ErrorRecord er = new ErrorRecord(ex, "WebCmdletIEDomNotSupportedException", ErrorCategory.NotImplemented, null); + ThrowTerminatingError(er); + } + } + + /// + /// Implementing ^C, after start the BeginGetResponse + /// + protected override void StopProcessing() + { + if (cancelToken != null) + { + cancelToken.Cancel(); + } + } + + #endregion Overrides + + #region Helper Methods + + /// + /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. + /// + /// The WebRequest who's content is to be set + /// A byte array containing the content data. + /// The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property + /// + /// Because this function sets the request's ContentLength property and writes content data into the requests's stream, + /// it should be called one time maximum on a given request. + /// + internal long SetRequestContent(HttpRequestMessage request, Byte[] content) + { + if (request == null) + throw new ArgumentNullException("request"); + if (content == null) + return 0; + + var byteArrayContent = new ByteArrayContent(content); + request.Content = byteArrayContent; + + return byteArrayContent.Headers.ContentLength.Value; + } + + /// + /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. + /// + /// The WebRequest who's content is to be set + /// A String object containing the content data. + /// The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property + /// + /// Because this function sets the request's ContentLength property and writes content data into the requests's stream, + /// it should be called one time maximum on a given request. + /// + internal long SetRequestContent(HttpRequestMessage request, string content) + { + if (request == null) + throw new ArgumentNullException("request"); + + if (content == null) + return 0; + + Encoding encoding = null; + if (ContentType != null) + { + // If Content-Type contains the encoding format (as CharSet), use this encoding format + // to encode the Body of the WebRequest sent to the server. Default Encoding format + // would be used if Charset is not supplied in the Content-Type property. + var mediaTypeHeaderValue = new MediaTypeHeaderValue(ContentType); + if (!string.IsNullOrEmpty(mediaTypeHeaderValue.CharSet)) + { + try + { + encoding = Encoding.GetEncoding(mediaTypeHeaderValue.CharSet); + } + catch (ArgumentException ex) + { + ErrorRecord er = new ErrorRecord(ex, "WebCmdletEncodingException", ErrorCategory.InvalidArgument, ContentType); + ThrowTerminatingError(er); + } + } + } + + Byte[] bytes = StreamHelper.EncodeToBytes(content, encoding); + var byteArrayContent = new ByteArrayContent(bytes); + request.Content = byteArrayContent; + + return byteArrayContent.Headers.ContentLength.Value; + } + + internal long SetRequestContent(HttpRequestMessage request, XmlNode xmlNode) + { + if (request == null) + throw new ArgumentNullException("request"); + + if (xmlNode == null) + return 0; + + Byte[] bytes = null; + XmlDocument doc = xmlNode as XmlDocument; + if (doc != null && (doc.FirstChild as XmlDeclaration) != null) + { + XmlDeclaration decl = doc.FirstChild as XmlDeclaration; + Encoding encoding = Encoding.GetEncoding(decl.Encoding); + bytes = StreamHelper.EncodeToBytes(doc.OuterXml, encoding); + } + else + { + bytes = StreamHelper.EncodeToBytes(xmlNode.OuterXml); + } + + var byteArrayContent = new ByteArrayContent(bytes); + request.Content = byteArrayContent; + + return byteArrayContent.Headers.ContentLength.Value; + } + + /// + /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. + /// + /// The WebRequest who's content is to be set + /// A Stream object containing the content data. + /// The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property + /// + /// Because this function sets the request's ContentLength property and writes content data into the requests's stream, + /// it should be called one time maximum on a given request. + /// + internal long SetRequestContent(HttpRequestMessage request, Stream contentStream) + { + if (request == null) + throw new ArgumentNullException("request"); + if (contentStream == null) + throw new ArgumentNullException("contentStream"); + + var streamContent = new StreamContent(contentStream); + request.Content = streamContent; + + return streamContent.Headers.ContentLength.Value; + } + + internal long SetRequestContent(HttpRequestMessage request, IDictionary content) + { + if (request == null) + throw new ArgumentNullException("request"); + if (content == null) + throw new ArgumentNullException("content"); + + string body = FormatDictionary(content); + return (SetRequestContent(request, body)); + + } + +#endregion Helper Methods + } +} +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseHelper.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseHelper.CoreClr.cs new file mode 100644 index 0000000000..8cd466de84 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseHelper.CoreClr.cs @@ -0,0 +1,46 @@ +#if CORECLR + +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System.Net.Http; +using System.Globalization; + +namespace Microsoft.PowerShell.Commands +{ + internal static partial class WebResponseHelper + { + internal static string GetCharacterSet(HttpResponseMessage response) + { + string characterSet = response.Content.Headers.ContentType.CharSet; + return characterSet; + } + + internal static string GetProtocol(HttpResponseMessage response) + { + string protocol = string.Format(CultureInfo.InvariantCulture, + "HTTP/{0}", response.Version); + return protocol; + } + + internal static int GetStatusCode(HttpResponseMessage response) + { + int statusCode = (int) response.StatusCode; + return statusCode; + } + + internal static string GetStatusDescription(HttpResponseMessage response) + { + string statusDescription = response.StatusCode.ToString(); + return statusDescription; + } + + internal static bool IsText(HttpResponseMessage response) + { + string contentType = response.Content.Headers.ContentType.MediaType; + return ContentHelper.IsText(contentType); + } + } +} +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs new file mode 100644 index 0000000000..1ea26bec08 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs @@ -0,0 +1,118 @@ +#if CORECLR + +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Text; +using System.Net.Http; +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// WebResponseObject + /// + public partial class WebResponseObject + { + #region Properties + + /// + /// gets or sets the BaseResponse property + /// + public HttpResponseMessage BaseResponse { get; set; } + + /// + /// gets the Headers property + /// + public Dictionary> Headers + { + get + { + var headers = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (var entry in BaseResponse.Headers) + { + headers[entry.Key] = entry.Value; + } + + return headers; + } + } + + #endregion + + #region Constructors + + /// + /// Constructor for WebResponseObject + /// + /// + public WebResponseObject(HttpResponseMessage response) + : this(response, null) { } + + /// + /// Constructor for WebResponseObject with contentStream + /// + /// + /// + public WebResponseObject(HttpResponseMessage response, Stream contentStream) + { + SetResponse(response, contentStream); + InitializeContent(); + InitializeRawContent(response); + } + + #endregion Constructors + + #region Methods + + private void InitializeRawContent(HttpResponseMessage baseResponse) + { + StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); + + // Use ASCII encoding for the RawContent visual view of the content. + if (Content.Length > 0) + { + raw.Append(this.ToString()); + } + + this.RawContent = raw.ToString(); + } + + private void SetResponse(HttpResponseMessage response, Stream contentStream) + { + if (null == response) { throw new ArgumentNullException("response"); } + + BaseResponse = response; + + MemoryStream ms = contentStream as MemoryStream; + if (null != ms) + { + _rawContentStream = ms; + } + else + { + Stream st = contentStream; + if (contentStream == null) + { + st = StreamHelper.GetResponseStream(response); + } + + long contentLength = response.Content.Headers.ContentLength.Value; + if (0 >= contentLength) + { + contentLength = StreamHelper.DefaultReadBuffer; + } + int initialCapacity = (int)Math.Min(contentLength, StreamHelper.DefaultReadBuffer); + _rawContentStream = new WebResponseContentMemoryStream(st, initialCapacity, null); + } + // set the position of the content stream to the beginning + _rawContentStream.Position = 0; + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObjectFactory.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObjectFactory.CoreClr.cs new file mode 100644 index 0000000000..f706ae5a14 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObjectFactory.CoreClr.cs @@ -0,0 +1,40 @@ +#if CORECLR + +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System.Net.Http; +using System.IO; +using System.Management.Automation; + +namespace Microsoft.PowerShell.Commands +{ + internal static class WebResponseObjectFactory + { + internal static WebResponseObject GetResponseObject(HttpResponseMessage response, Stream responseStream, ExecutionContext executionContext, bool useBasicParsing = false) + { + WebResponseObject output; + if (WebResponseHelper.IsText(response)) + { + output = new BasicHtmlWebResponseObject(response, responseStream); + + // TODO: This code needs to be enable after the dependency on mshtml is resolved. + //if (useBasicParsing) + //{ + // output = new BasicHtmlWebResponseObject(response, responseStream); + //} + //else + //{ + // output = new HtmlWebResponseObject(response, responseStream, executionContext); + //} + } + else + { + output = new WebResponseObject(response, responseStream); + } + return (output); + } + } +} +#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObjectCollection.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObjectCollection.cs index 96dac781cb..3044fcb796 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObjectCollection.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObjectCollection.cs @@ -4,8 +4,6 @@ Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Collections.ObjectModel; -using System.Linq; -using System.Text; namespace Microsoft.PowerShell.Commands { diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/BasicHtmlWebResponseObject.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/BasicHtmlWebResponseObject.FullClr.cs new file mode 100644 index 0000000000..c8e60f2b8d --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/BasicHtmlWebResponseObject.FullClr.cs @@ -0,0 +1,50 @@ +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ +using System.Net; +using System.IO; +using System.Text; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Response object for html content without DOM parsing + /// + public partial class BasicHtmlWebResponseObject : WebResponseObject + { + #region Constructors + + /// + /// Constructor for HtmlWebResponseObject + /// + /// + public BasicHtmlWebResponseObject(WebResponse response) + : this(response, null) { } + + /// + /// Constructor for HtmlWebResponseObject with memory stream + /// + /// + /// + public BasicHtmlWebResponseObject(WebResponse response, Stream contentStream) + : base(response, contentStream) + { + EnsureHtmlParser(); + InitializeContent(); + InitializeRawContent(response); + } + + #endregion Constructors + + #region Methods + + private void InitializeRawContent(WebResponse baseResponse) + { + StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); + raw.Append(Content); + this.RawContent = raw.ToString(); + } + + #endregion Methods + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/ContentHelper.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/ContentHelper.FullClr.cs new file mode 100644 index 0000000000..29a6702438 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/ContentHelper.FullClr.cs @@ -0,0 +1,65 @@ +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Text; +using System.Net; + +namespace Microsoft.PowerShell.Commands +{ + internal static partial class ContentHelper + { + internal static Encoding GetEncoding(WebResponse response) + { + string characterSet = null; + HttpWebResponse httpResponse = response as HttpWebResponse; + if (null != httpResponse) + { + characterSet = httpResponse.CharacterSet; + } + + return GetEncodingOrDefault(characterSet); + } + + // Gets the content type with safe fallback - in the situation + // of FTPWebResponse that retuns NotImplemented. + internal static string GetContentType(WebResponse response) + { + string contentType = null; + try + { + contentType = response.ContentType; + } + catch (NotImplementedException) { } + + return contentType; + } + + internal static StringBuilder GetRawContentHeader(WebResponse baseResponse) + { + StringBuilder raw = new StringBuilder(); + + // add protocol and status line + string protocol = WebResponseHelper.GetProtocol(baseResponse); + if (!String.IsNullOrEmpty(protocol)) + { + int statusCode = WebResponseHelper.GetStatusCode(baseResponse); + string statusDescription = WebResponseHelper.GetStatusDescription(baseResponse); + raw.AppendFormat("{0} {1} {2}", protocol, statusCode, statusDescription); + raw.AppendLine(); + } + + // add headers + foreach (string key in baseResponse.Headers.AllKeys) + { + string value = baseResponse.Headers[key]; + raw.AppendFormat("{0}: {1}", key, value); + raw.AppendLine(); + } + + raw.AppendLine(); + return raw; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/HtmlWebResponseObject.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/HtmlWebResponseObject.FullClr.cs new file mode 100644 index 0000000000..df86744cd6 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/HtmlWebResponseObject.FullClr.cs @@ -0,0 +1,64 @@ +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Management.Automation; +using System.Net; +using System.IO; +using System.Text; +using ExecutionContext = System.Management.Automation.ExecutionContext; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Response object for html content + /// + public partial class HtmlWebResponseObject : WebResponseObject, IDisposable + { + #region Constructors + + /// + /// Constructor for HtmlWebResponseObject + /// + /// + /// + internal HtmlWebResponseObject(WebResponse response, ExecutionContext executionContext) + : this(response, null, executionContext) { } + + /// + /// Constructor for HtmlWebResponseObject with memory stream + /// + /// + /// + /// + internal HtmlWebResponseObject(WebResponse response, Stream contentStream, ExecutionContext executionContext) + : base(response, contentStream) + { + if (executionContext == null) + { + throw PSTraceSource.NewArgumentNullException("executionContext"); + } + + _executionContext = executionContext; + InitializeContent(); + InitializeRawContent(response); + } + + #endregion Constructors + + #region Methods + + private void InitializeRawContent(WebResponse baseResponse) + { + StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); + if (null != Content) + { + raw.Append(Content); + } + this.RawContent = raw.ToString(); + } + + #endregion + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeRestMethodCommand.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeRestMethodCommand.FullClr.cs new file mode 100644 index 0000000000..870fc32a25 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeRestMethodCommand.FullClr.cs @@ -0,0 +1,112 @@ +/********************************************************************++ +Copyright (c) Microsoft Corporation. All rights reserved. +--********************************************************************/ + +using System; +using System.Management.Automation; +using System.Net; +using System.Collections; +using System.IO; +using System.Xml; +using System.Text; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// The Invoke-RestMethod command + /// This command makes an HTTP or HTTPS request to a web service, + /// and returns the response in an appropriate way. + /// Intended to work against the wide spectrum of "RESTful" web services + /// currently deployed across the web. + /// + [Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=217034")] + public partial class InvokeRestMethodCommand : WebRequestPSCmdlet + { + #region Virtual Method Overrides + + /// + /// Process the web reponse and output corresponding objects. + /// + /// + internal override void ProcessResponse(WebResponse response) + { + if (null == response) { throw new ArgumentNullException("response"); } + + using (BufferingStreamReader responseStream = new BufferingStreamReader(StreamHelper.GetResponseStream(response))) + { + if (ShouldWriteToPipeline) + { + // First see if it is an RSS / ATOM feed, in which case we can + // stream it - unless the user has overriden it with a return type of "XML" + if (TryProcessFeedStream(responseStream)) + { + // Do nothing, content has been processed. + } + else + { + // determine the response type + RestReturnType returnType = CheckReturnType(response); + // get the response encoding + Encoding encoding = ContentHelper.GetEncoding(response); + + object obj = null; + Exception ex = null; + + string str = StreamHelper.DecodeStream(responseStream, encoding); + bool convertSuccess = false; + if (returnType == RestReturnType.Json) + { + convertSuccess = TryConvertToJson(str, out obj, ref ex) || TryConvertToXml(str, out obj, ref ex); + } + // default to try xml first since it's more common + else + { + convertSuccess = TryConvertToXml(str, out obj, ref ex) || TryConvertToJson(str, out obj, ref ex); + } + + if (!convertSuccess) + { + // fallback to string + obj = str; + } + + WriteObject(obj); + } + } + + if (ShouldSaveToOutFile) + { + StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this); + } + } + } + + #endregion Virtual Method Overrides + + #region Helper Methods + + private RestReturnType CheckReturnType(WebResponse response) + { + if (null == response) { throw new ArgumentNullException("response"); } + + RestReturnType rt = RestReturnType.Detect; + string contentType = ContentHelper.GetContentType(response); + if (string.IsNullOrEmpty(contentType)) + { + rt = RestReturnType.Detect; + } + else if (ContentHelper.IsJson(contentType)) + { + rt = RestReturnType.Json; + } + else if (ContentHelper.IsXml(contentType)) + { + rt = RestReturnType.Xml; + } + + return (rt); + } + + #endregion Helper Methods + } +} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/InvokeWebRequestCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeWebRequestCommand.FullClr.cs similarity index 90% rename from src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/InvokeWebRequestCommand.cs rename to src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeWebRequestCommand.FullClr.cs index beb9819c2d..963505dde3 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/InvokeWebRequestCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeWebRequestCommand.FullClr.cs @@ -1,4 +1,4 @@ -/********************************************************************++ +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ @@ -6,11 +6,6 @@ using System; using System.Management.Automation; using System.Net; using System.IO; -using System.Text; -using System.Diagnostics.CodeAnalysis; -using System.Collections; -using Microsoft.Win32; -using System.Security.Cryptography.X509Certificates; namespace Microsoft.PowerShell.Commands { @@ -60,4 +55,4 @@ namespace Microsoft.PowerShell.Commands #endregion Virtual Method Overrides } -} +} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObjectTypeResolver.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/JsonObjectTypeResolver.cs similarity index 91% rename from src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObjectTypeResolver.cs rename to src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/JsonObjectTypeResolver.cs index a279a6df1d..142b64c1d7 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObjectTypeResolver.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/JsonObjectTypeResolver.cs @@ -1,4 +1,4 @@ -/********************************************************************++ +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ @@ -26,4 +26,4 @@ namespace Microsoft.PowerShell.Commands return (string.Empty); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestPSCmdlet.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebRequestPSCmdlet.FullClr.cs similarity index 59% rename from src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestPSCmdlet.cs rename to src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebRequestPSCmdlet.FullClr.cs index 041147285b..97f74fff11 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestPSCmdlet.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebRequestPSCmdlet.FullClr.cs @@ -3,28 +3,24 @@ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System; -using System.Collections.ObjectModel; using System.Management.Automation; using System.Net; using System.IO; using System.Text; -using System.Diagnostics; using System.Collections; using System.Globalization; using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Web; using System.Xml; using mshtml; -using Microsoft.Win32; namespace Microsoft.PowerShell.Commands { /// /// Base class for Invoke-RestMethod and Invoke-WebRequest commands. /// - public abstract class WebRequestPSCmdlet : PSCmdlet + public abstract partial class WebRequestPSCmdlet : PSCmdlet { #region Abstract Methods @@ -37,443 +33,17 @@ namespace Microsoft.PowerShell.Commands #endregion Abstract Methods - #region Virtual Properties - - #region URI - - /// - /// gets or sets the parameter UseBasicParsing - /// - [Parameter] - public virtual SwitchParameter UseBasicParsing { get; set; } - - /// - /// gets or sets the Uri property - /// - [Parameter(Position = 0, Mandatory = true)] - [ValidateNotNullOrEmpty] - public virtual Uri Uri { get; set; } - - #endregion - - #region Session - /// - /// gets or sets the Session property - /// - [Parameter] - public virtual WebRequestSession WebSession { get; set; } - - /// - /// gets or sets the SessionVariable property - /// - [Parameter] - [Alias("SV")] - public virtual string SessionVariable { get; set; } - - #endregion - - #region Authorization and Credentials - - /// - /// gets or sets the Credential property - /// - [Parameter] - [Credential] - public virtual PSCredential Credential { get; set; } - - /// - /// gets or sets the UseDefaultCredentials property - /// - [Parameter] - public virtual SwitchParameter UseDefaultCredentials { get; set; } - - /// - /// gets or sets the CertificateThumbprint property - /// - [Parameter] - [ValidateNotNullOrEmpty] - public virtual string CertificateThumbprint { get; set; } - - /// - /// gets or sets the Certificate property - /// - [Parameter] - [ValidateNotNull] - public virtual X509Certificate Certificate { get; set; } - - #endregion - - #region Headers - - /// - /// gets or sets the UserAgent property - /// - [Parameter] - public virtual string UserAgent { get; set; } - - /// - /// gets or sets the DisableKeepAlive property - /// - [Parameter] - public virtual SwitchParameter DisableKeepAlive { get; set; } - - /// - /// gets or sets the TimeOut property - /// - [Parameter] - public virtual int TimeoutSec { get; set; } - - /// - /// gets or sets the Headers property - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - [Parameter] - public virtual IDictionary Headers { get; set; } - - #endregion - - #region Redirect - - /// - /// gets or sets the RedirectMax property - /// - [Parameter] - [ValidateRange(0, Int32.MaxValue)] - public virtual int MaximumRedirection - { - get { return maximumRedirection; } - set { maximumRedirection = value; } - } - private int maximumRedirection = -1; - - #endregion - - #region Method - - /// - /// gets or sets the Method property - /// - [Parameter] - public virtual WebRequestMethod Method - { - get { return method; } - set { method = value; } - } - private WebRequestMethod method = WebRequestMethod.Default; - - #endregion - - #region Proxy - - /// - /// gets or sets the Proxy property - /// - [Parameter] - public virtual Uri Proxy { get; set; } - - /// - /// gets or sets the ProxyCredential property - /// - [Parameter] - [Credential] - public virtual PSCredential ProxyCredential { get; set; } - - /// - /// gets or sets the ProxyUseDefaultCredentials property - /// - [Parameter] - public virtual SwitchParameter ProxyUseDefaultCredentials { get; set; } - - #endregion - - #region Input - - /// - /// gets or sets the Body property - /// - [Parameter(ValueFromPipeline = true)] - public virtual object Body { get; set; } - - /// - /// gets or sets the ContentType property - /// - [Parameter] - public virtual string ContentType { get; set; } - - /// - /// gets or sets the TransferEncoding property - /// - [Parameter] - [ValidateSet("chunked", "compress", "deflate", "gzip", "identity", IgnoreCase = true)] - public virtual string TransferEncoding { get; set; } - - /// - /// gets or sets the InFile property - /// - [Parameter] - public virtual string InFile { get; set; } - - /// - /// keep the original file path after the resolved provider path is - /// assigned to InFile - /// - private string _originalFilePath; - /// /// the WebRequest we will GetResponse for. /// private WebRequest _webRequest; - #endregion - - #region Output - - /// - /// gets or sets the OutFile property - /// - [Parameter] - public virtual string OutFile { get; set; } - - /// - /// gets or sets the PassThrough property - /// - [Parameter] - public virtual SwitchParameter PassThru { get; set; } - - #endregion - - #endregion Virtual Properties - #region Virtual Methods - internal virtual void ValidateParameters() - { - // sessions - if ((null != WebSession) && (null != SessionVariable)) - { - ErrorRecord error = GetValidationError(WebCmdletStrings.SessionConflict, - "WebCmdletSessionConflictException"); - ThrowTerminatingError(error); - } - - // credentials - if (UseDefaultCredentials && (null != Credential)) - { - ErrorRecord error = GetValidationError(WebCmdletStrings.CredentialConflict, - "WebCmdletCredentialConflictException"); - ThrowTerminatingError(error); - } - - // Proxy server - if (ProxyUseDefaultCredentials && (null != ProxyCredential)) - { - ErrorRecord error = GetValidationError(WebCmdletStrings.ProxyCredentialConflict, - "WebCmdletProxyCredentialConflictException"); - ThrowTerminatingError(error); - } - else if ((null == Proxy) && ((null != ProxyCredential) || ProxyUseDefaultCredentials)) - { - ErrorRecord error = GetValidationError(WebCmdletStrings.ProxyUriNotSupplied, - "WebCmdletProxyUriNotSuppliedException"); - ThrowTerminatingError(error); - } - - // request body content - if ((null != Body) && (null != InFile)) - { - ErrorRecord error = GetValidationError(WebCmdletStrings.BodyConflict, - "WebCmdletBodyConflictException"); - ThrowTerminatingError(error); - } - - // validate InFile path - if (InFile != null) - { - ProviderInfo provider = null; - ErrorRecord errorRecord = null; - - try - { - Collection providerPaths = GetResolvedProviderPathFromPSPath(InFile, out provider); - - if (!provider.Name.Equals(FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) - { - errorRecord = GetValidationError(WebCmdletStrings.NotFilesystemPath, - "WebCmdletInFileNotFilesystemPathException", InFile); - } - else - { - if (providerPaths.Count > 1) - { - errorRecord = GetValidationError(WebCmdletStrings.MultiplePathsResolved, - "WebCmdletInFileMultiplePathsResolvedException", InFile); - } - else if (providerPaths.Count == 0) - { - errorRecord = GetValidationError(WebCmdletStrings.NoPathResolved, - "WebCmdletInFileNoPathResolvedException", InFile); - } - else - { - if (Directory.Exists(providerPaths[0])) - { - errorRecord = GetValidationError(WebCmdletStrings.DirecotryPathSpecified, - "WebCmdletInFileNotFilePathException", InFile); - } - _originalFilePath = InFile; - InFile = providerPaths[0]; - } - } - } - catch (ItemNotFoundException pathNotFound) - { - errorRecord = new ErrorRecord(pathNotFound.ErrorRecord, pathNotFound); - } - catch (ProviderNotFoundException providerNotFound) - { - errorRecord = new ErrorRecord(providerNotFound.ErrorRecord, providerNotFound); - } - catch (System.Management.Automation.DriveNotFoundException driveNotFound) - { - errorRecord = new ErrorRecord(driveNotFound.ErrorRecord, driveNotFound); - } - - if (errorRecord != null) - { - ThrowTerminatingError(errorRecord); - } - } - - // output ?? - if (PassThru && (null == OutFile)) - { - ErrorRecord error = GetValidationError(WebCmdletStrings.OutFileMissing, - "WebCmdletOutFileMissingException"); - ThrowTerminatingError(error); - } - } - - internal virtual void PrepareSession() - { - // make sure we have a valid WebRequestSession object to work with - if (null == WebSession) - { - WebSession = new WebRequestSession(); - } - - if (null != SessionVariable) - { - // save the session back to the PS environment if requested - PSVariableIntrinsics vi = SessionState.PSVariable; - vi.Set(SessionVariable, WebSession); - } - - // - // handle credentials - // - if (null != Credential) - { - // get the relevant NetworkCredential - NetworkCredential netCred = Credential.GetNetworkCredential(); - WebSession.Credentials = netCred; - - // supplying a credential overrides the UseDefaultCredentials setting - WebSession.UseDefaultCredentials = false; - } - else if (UseDefaultCredentials) - { - WebSession.UseDefaultCredentials = true; - } - - - if (null != CertificateThumbprint) - { - X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); - store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); - X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates; - X509Certificate2Collection tbCollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByThumbprint, CertificateThumbprint, false); - if (tbCollection.Count == 0) - { - CryptographicException ex = new CryptographicException(WebCmdletStrings.ThumbprintNotFound); - throw ex; - } - foreach (X509Certificate2 tbCert in tbCollection) - { - X509Certificate certificate = (X509Certificate)tbCert; - WebSession.AddCertificate(certificate); - } - } - - if (null != Certificate) - { - WebSession.AddCertificate(Certificate); - } - - // - // handle the user agent - // - if (null != UserAgent) - { - // store the UserAgent string - WebSession.UserAgent = UserAgent; - } - - if (null != Proxy) - { - WebProxy webProxy = new WebProxy(Proxy); - webProxy.BypassProxyOnLocal = false; - if (null != ProxyCredential) - { - webProxy.Credentials = ProxyCredential.GetNetworkCredential(); - webProxy.UseDefaultCredentials = false; - } - else if (ProxyUseDefaultCredentials) - { - webProxy.UseDefaultCredentials = true; - } - WebSession.Proxy = webProxy; - } - - if (-1 < MaximumRedirection) - { - WebSession.MaximumRedirection = MaximumRedirection; - } - - // store the other supplied headers - if (null != Headers) - { - foreach (string key in Headers.Keys) - { - // add the header value (or overwrite it if already present) - WebSession.Headers[key] = Headers[key].ToString(); - } - } - } - internal virtual WebRequest GetRequest(Uri uri) { + uri = PrepareUri(uri); // create the base WebRequest object - uri = CheckProtocol(uri); - // before creating the web request, - // preprocess Body if content is a dictionary and method is GET (set as query) - IDictionary bodyAsDictionary; - LanguagePrimitives.TryConvertTo(Body, out bodyAsDictionary); - if ((null != bodyAsDictionary) - && (Method == WebRequestMethod.Default || Method == WebRequestMethod.Get)) - { - UriBuilder uriBuilder = new UriBuilder(uri); - if (uriBuilder.Query != null && uriBuilder.Query.Length > 1) - { - uriBuilder.Query = uriBuilder.Query.Substring(1) + "&" + FormatDictionary(bodyAsDictionary); - } - else - { - uriBuilder.Query = FormatDictionary(bodyAsDictionary); - } - uri = uriBuilder.Uri; - // set body to null to prevent later FillRequestStream - Body = null; - } WebRequest request = WebRequest.Create(uri); // pull in session data @@ -545,7 +115,12 @@ namespace Microsoft.PowerShell.Commands } // check timeout setting (in seconds instead of milliseconds as in HttpWebRequest) - if (0 < TimeoutSec) + if (TimeoutSec == 0) + { + // A zero timeout means infinite + httpRequest.Timeout = Timeout.Infinite; + } + else if (TimeoutSec > 0) { // just to make sure if (TimeoutSec > Int32.MaxValue / 1000) @@ -570,7 +145,6 @@ namespace Microsoft.PowerShell.Commands httpRequest.SendChunked = true; httpRequest.TransferEncoding = TransferEncoding; } - } return (request); @@ -962,25 +536,6 @@ namespace Microsoft.PowerShell.Commands #endregion Overrides - #region Helper Properties - - internal string QualifiedOutFile - { - get { return (QualifyFilePath(OutFile)); } - } - - internal bool ShouldSaveToOutFile - { - get { return (!string.IsNullOrEmpty(OutFile)); } - } - - internal bool ShouldWriteToPipeline - { - get { return (!ShouldSaveToOutFile || PassThru); } - } - - #endregion Helper Properties - #region Helper Methods /// @@ -1024,24 +579,6 @@ namespace Microsoft.PowerShell.Commands } } - private string QualifyFilePath(string path) - { - string resolvedFilePath = PathUtils.ResolveFilePath(path, this, false); - return resolvedFilePath; - } - - private const string DefaultProtocol = "http://"; - private Uri CheckProtocol(Uri uri) - { - if (null == uri) { throw new ArgumentNullException("uri"); } - - if (!uri.IsAbsoluteUri) - { - uri = new Uri(DefaultProtocol + uri.OriginalString); - } - return (uri); - } - /// /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. /// @@ -1200,121 +737,6 @@ namespace Microsoft.PowerShell.Commands } - /// - /// Verifies that Internet Explorer is available, and that its first-run - /// configuration is complete. - /// - /// True if we should try to access IE's COM object. Not - /// needed if an HtmlDocument will be created shortly. - protected void VerifyInternetExplorerAvailable(bool checkComObject) - { - bool isInternetExplorerConfigurationComplete = false; - - // The registry key DisableFirstRunCustomize can exits at one of the following path. - // IE uses the same decending orider (as mentioned) to check for the presence of this key. - // If the value of DisableFirstRunCustomize key is set to greater than zero then Run first - // is disabled. - string[] disableFirstRunCustomizePaths = new string[] { - @"HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Internet Explorer\Main", - @"HKEY_CURRENT_USER\Software\Policies\Microsoft\Internet Explorer\Main", - @"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main", - @"HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Main" }; - - foreach (string currentRegPath in disableFirstRunCustomizePaths) - { - object val = Registry.GetValue(currentRegPath, "DisableFirstRunCustomize", string.Empty); - if (val != null && !String.Empty.Equals(val) && Convert.ToInt32(val, CultureInfo.InvariantCulture) > 0) - { - isInternetExplorerConfigurationComplete = true; - break; - } - } - - if (!isInternetExplorerConfigurationComplete) - { - // Verify that if IE is installed, it has been through the RunOnce check. - // Otherwise, the call will hang waiting for users to go through First Run - // personalization. - using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Internet Explorer\\Main")) - { - if (key == null) - { - throw new NotSupportedException(WebCmdletStrings.IEDomNotSupported); - } - - foreach (string setting in key.GetValueNames()) - { - if (setting.IndexOf("RunOnce", StringComparison.OrdinalIgnoreCase) > -1) - { - isInternetExplorerConfigurationComplete = true; - break; - } - } - } - } - - if (!isInternetExplorerConfigurationComplete) - { - throw new NotSupportedException(WebCmdletStrings.IEDomNotSupported); - } - - if (checkComObject) - { - try - { - mshtml.IHTMLDocument2 ieCheck = (mshtml.IHTMLDocument2) new mshtml.HTMLDocument(); - System.Runtime.InteropServices.Marshal.ReleaseComObject(ieCheck); - } - catch (System.Runtime.InteropServices.COMException) - { - throw new NotSupportedException(WebCmdletStrings.IEDomNotSupported); - } - } - } - - private string FormatDictionary(IDictionary content) - { - if (content == null) - throw new ArgumentNullException("content"); - - StringBuilder bodyBuilder = new StringBuilder(); - foreach (string key in content.Keys) - { - if (0 < bodyBuilder.Length) - { - bodyBuilder.Append("&"); - } - - object value = content[key]; - - // URLEncode the key and value - string encodedKey = HttpUtility.UrlEncode(key); - string encodedValue = String.Empty; - if (null != value) - { - encodedValue = HttpUtility.UrlEncode(value.ToString()); - } - - bodyBuilder.AppendFormat("{0}={1}", encodedKey, encodedValue); - } - return bodyBuilder.ToString(); - } - - private ErrorRecord GetValidationError(string msg, string errorId) - { - var ex = new ValidationMetadataException(msg); - var error = new ErrorRecord(ex, errorId, ErrorCategory.InvalidArgument, this); - return (error); - } - - private ErrorRecord GetValidationError(string msg, string errorId, params object[] args) - { - msg = string.Format(CultureInfo.InvariantCulture, msg, args); - var ex = new ValidationMetadataException(msg); - var error = new ErrorRecord(ex, errorId, ErrorCategory.InvalidArgument, this); - return (error); - } - #endregion Helper Methods #region private State class @@ -1355,4 +777,4 @@ namespace Microsoft.PowerShell.Commands #endregion private State class } -} +} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebResponseHelper.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseHelper.FullClr.cs similarity index 93% rename from src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebResponseHelper.cs rename to src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseHelper.FullClr.cs index d8afb0a7c4..cf99c83e57 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebResponseHelper.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseHelper.FullClr.cs @@ -1,15 +1,14 @@ -/********************************************************************++ +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System; using System.Net; using System.Globalization; -using System.Reflection; namespace Microsoft.PowerShell.Commands { - internal static class WebResponseHelper + internal static partial class WebResponseHelper { internal static string GetCharacterSet(WebResponse response) { @@ -70,4 +69,4 @@ namespace Microsoft.PowerShell.Commands return (ContentHelper.IsText(contentType)); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebResponseObject.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObject.FullClr.cs similarity index 53% rename from src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebResponseObject.cs rename to src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObject.FullClr.cs index 7791ad00cb..67182e4af3 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebResponseObject.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObject.FullClr.cs @@ -3,7 +3,6 @@ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System; -using System.Diagnostics.CodeAnalysis; using System.Text; using System.Net; using System.Collections.Generic; @@ -14,41 +13,19 @@ namespace Microsoft.PowerShell.Commands /// /// WebResponseObject /// - public class WebResponseObject + public partial class WebResponseObject { #region Properties - /// - /// gets or protected sets the Content property - /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public byte[] Content { get; protected set; } - /// /// gets or sets the BaseResponse property /// public WebResponse BaseResponse { get; set; } - /// - /// gets the StatusCode property - /// - public int StatusCode - { - get { return (WebResponseHelper.GetStatusCode(BaseResponse)); } - } - - /// - /// gets the StatusDescription property - /// - public string StatusDescription - { - get { return (WebResponseHelper.GetStatusDescription(BaseResponse)); } - } - /// /// gets the Headers property /// - public Dictionary Headers + public Dictionary Headers { get { @@ -62,29 +39,7 @@ namespace Microsoft.PowerShell.Commands } } - private MemoryStream _rawContentStream; - /// - /// gets the RawContentStream property - /// - public MemoryStream RawContentStream - { - get { return (_rawContentStream); } - } - - /// - /// gets the RawContentLength property - /// - public long RawContentLength - { - get { return (null == RawContentStream ? -1 : RawContentStream.Length); } - } - - /// - /// gets or protected sets the RawContent property - /// - public string RawContent { get; protected set; } - - #endregion Properties + #endregion #region Constructors @@ -102,7 +57,7 @@ namespace Microsoft.PowerShell.Commands /// public WebResponseObject(WebResponse response, Stream contentStream) { - SetResponse(response, contentStream); + SetResponse(response, contentStream); InitializeContent(); InitializeRawContent(response); } @@ -111,14 +66,6 @@ namespace Microsoft.PowerShell.Commands #region Methods - /// - /// Reads the response content from the web response. - /// - private void InitializeContent() - { - this.Content = this.RawContentStream.ToArray(); - } - private void InitializeRawContent(WebResponse baseResponse) { StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); @@ -126,21 +73,16 @@ namespace Microsoft.PowerShell.Commands // Use ASCII encoding for the RawContent visual view of the content. if (Content.Length > 0) { - raw.Append(this.ToString()); + raw.Append(this.ToString()); } this.RawContent = raw.ToString(); } - private bool IsPrintable(char c) - { - return (Char.IsLetterOrDigit(c) || Char.IsPunctuation(c) || Char.IsSeparator(c) || Char.IsSymbol(c) || Char.IsWhiteSpace(c)); - } - private void SetResponse(WebResponse response, Stream contentStream) { if (null == response) { throw new ArgumentNullException("response"); } - + BaseResponse = response; MemoryStream ms = contentStream as MemoryStream; @@ -149,7 +91,7 @@ namespace Microsoft.PowerShell.Commands _rawContentStream = ms; } else - { + { Stream st = contentStream; if (contentStream == null) { @@ -162,31 +104,13 @@ namespace Microsoft.PowerShell.Commands contentLength = StreamHelper.DefaultReadBuffer; } int initialCapacity = (int)Math.Min(contentLength, StreamHelper.DefaultReadBuffer); - _rawContentStream = new WebResponseContentMemoryStream(st, initialCapacity, null); + _rawContentStream = new WebResponseContentMemoryStream(st, initialCapacity, null); } // set the position of the content stream to the beginning _rawContentStream.Position = 0; } - /// - /// Returns the string representation of this web response. - /// - /// The string representation of this web response. - public sealed override string ToString() - { - char[] stringContent = System.Text.Encoding.ASCII.GetChars(Content); - for (int counter = 0; counter < stringContent.Length; counter++) - { - if (!IsPrintable(stringContent[counter])) - { - stringContent[counter] = '.'; - } - } - - return new string(stringContent); - } - - #endregion Methods + #endregion } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebResponseObjectFactory.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObjectFactory.FullClr.cs similarity index 81% rename from src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebResponseObjectFactory.cs rename to src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObjectFactory.FullClr.cs index e2019ae76f..d5b9f98e61 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebResponseObjectFactory.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObjectFactory.FullClr.cs @@ -1,7 +1,6 @@ -/********************************************************************++ +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ - using System; using System.Net; using System.IO; @@ -11,11 +10,6 @@ namespace Microsoft.PowerShell.Commands { internal static class WebResponseObjectFactory { - //internal static WebResponseObject GetResponseObject(WebResponse response) - //{ - // return GetResponseObject(response, null); - //} - internal static WebResponseObject GetResponseObject(WebResponse response, Stream responseStream, ExecutionContext executionContext, bool useBasicParsing = false) { WebResponseObject output; @@ -37,4 +31,4 @@ namespace Microsoft.PowerShell.Commands return (output); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/InvokeRestMethodCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/InvokeRestMethodCommand.cs deleted file mode 100644 index 31c2748b2d..0000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/InvokeRestMethodCommand.cs +++ /dev/null @@ -1,374 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; -using System.Net; -using System.Collections; -using System.IO; -using System.Xml; -using System.Text; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// The Invoke-RestMethod command - /// This command makes an HTTP or HTTPS request to a web service, - /// and returns the response in an appropriate way. - /// Intended to work against the wide spectrum of “RESTful” web services - /// currently deployed across the web. - /// - [Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=217034")] - public class InvokeRestMethodCommand : WebRequestPSCmdlet - { - #region Parameters - - /// - /// gets or sets the parameter Method - /// - [Parameter] - public override WebRequestMethod Method - { - get { return base.Method; } - set { base.Method = value; } - } - - #endregion Parameters - - #region Virtual Method Overrides - - /// - /// Process the web reponse and output corresponding objects. - /// - /// - internal override void ProcessResponse(WebResponse response) - { - if (null == response) { throw new ArgumentNullException("response"); } - - using (BufferingStreamReader responseStream = new BufferingStreamReader(StreamHelper.GetResponseStream(response))) - { - if (ShouldWriteToPipeline) - { - // First see if it is an RSS / ATOM feed, in which case we can - // stream it - unless the user has overriden it with a return type of "XML" - if (TryProcessFeedStream(responseStream)) - { - // Do nothing, content has been processed. - } - else - { - // determine the response type - RestReturnType returnType = CheckReturnType(response); - // get the response encoding - Encoding encoding = ContentHelper.GetEncoding(response); - - object obj = null; - Exception ex = null; - - string str = StreamHelper.DecodeStream(responseStream, encoding); - bool convertSuccess = false; - if (returnType == RestReturnType.Json) - { - convertSuccess = TryConvertToJson(str, out obj, ref ex) || TryConvertToXml(str, out obj, ref ex); - } - // default to try xml first since it's more common - else - { - convertSuccess = TryConvertToXml(str, out obj, ref ex) || TryConvertToJson(str, out obj, ref ex); - } - - if (!convertSuccess) - { - // fallback to string - obj = str; - } - - WriteObject(obj); - } - } - - if (ShouldSaveToOutFile) - { - StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this); - } - } - } - - private bool TryProcessFeedStream(BufferingStreamReader responseStream) - { - bool isRssOrFeed = false; - - try - { - XmlReaderSettings readerSettings = GetSecureXmlReaderSettings(); - XmlReader reader = XmlReader.Create(responseStream, readerSettings); - - // See if the reader contained an "RSS" or "Feed" in the first 10 elements (RSS and Feed are normally 2 or 3) - int readCount = 0; - while ((readCount < 10) && reader.Read()) - { - if (String.Equals("rss", reader.Name, StringComparison.OrdinalIgnoreCase) || - String.Equals("feed", reader.Name, StringComparison.OrdinalIgnoreCase)) - { - isRssOrFeed = true; - break; - } - - readCount++; - } - - if (isRssOrFeed) - { - XmlDocument workingDocument = new XmlDocument(); - // performing a Read() here to avoid rrechecking - // "rss" or "feed" itmes - reader.Read(); - while (!reader.EOF) - { - // If node is Elemen and - // it's the 'Item' node or Entry, emit that node. - if ((reader.NodeType == XmlNodeType.Element) && - (String.Equals("Item", reader.Name, StringComparison.OrdinalIgnoreCase) || - String.Equals("Entry", reader.Name, StringComparison.OrdinalIgnoreCase)) - ) - { - // this one will do reader.Read() internally - XmlNode result = workingDocument.ReadNode(reader); - WriteObject(result); - } - else - { - reader.Read(); - } - } - } - } - catch (XmlException) { } - finally - { - responseStream.Seek(0, SeekOrigin.Begin); - } - - return isRssOrFeed; - } - - // Mostly cribbed from Serialization.cs#GetXmlReaderSettingsForCliXml() - private XmlReaderSettings GetSecureXmlReaderSettings() - { - XmlReaderSettings xrs = new XmlReaderSettings(); - - xrs.CheckCharacters = false; - xrs.CloseInput = false; - - //The XML data needs to be in conformance to the rules for a well-formed XML 1.0 document. - xrs.IgnoreProcessingInstructions = true; - xrs.MaxCharactersFromEntities = 1024; - xrs.DtdProcessing = DtdProcessing.Ignore; - - return xrs; - } - - - #endregion Virtual Method Overrides - - #region Helper Methods - - private bool TryConvertToXml(string xml, out object doc, ref Exception exRef) - { - try - { - XmlReaderSettings settings = GetSecureXmlReaderSettings(); - XmlReader xmlReader = XmlReader.Create(new StringReader(xml), settings); - doc = new XmlDocument(); - ((XmlDocument) doc).PreserveWhitespace = true; - ((XmlDocument) doc).Load(xmlReader); - } - catch (XmlException ex) - { - exRef = ex; - doc = null; - } - return (null != doc); - } - - private bool TryConvertToJson(string json, out object obj, ref Exception exRef) - { - try - { - ErrorRecord error; - obj = JsonObject.ConvertFromJson(json, out error); - - if (error != null) - { - exRef = error.Exception; - obj = null; - } - } - catch (ArgumentException ex) - { - exRef = ex; - obj = null; - } - catch (InvalidOperationException ex) - { - exRef = ex; - obj = null; - } - return (null != obj); - } - - private RestReturnType CheckReturnType(WebResponse response) - { - if (null == response) { throw new ArgumentNullException("response"); } - - RestReturnType rt = RestReturnType.Detect; - string contentType = ContentHelper.GetContentType(response); - if (string.IsNullOrEmpty(contentType)) - { - rt = RestReturnType.Detect; - } - else if (ContentHelper.IsJson(contentType)) - { - rt = RestReturnType.Json; - } - else if (ContentHelper.IsXml(contentType)) - { - rt = RestReturnType.Xml; - } - - return (rt); - } - - #endregion Helper Methods - } - - /// - /// enum for rest return type. - /// - public enum RestReturnType - { - /// - /// Return type not defined in reponse, - /// best effort detect - /// - Detect, - - /// - /// Json return type - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - Json, - - /// - /// Xml return type - /// - Xml, - } - - internal class BufferingStreamReader : Stream - { - internal BufferingStreamReader(Stream baseStream) - { - this.baseStream = baseStream; - streamBuffer = new MemoryStream(); - this.length = long.MaxValue; - copyBuffer = new byte[4096]; - } - - private Stream baseStream; - private MemoryStream streamBuffer; - private byte[] copyBuffer; - - public override bool CanRead - { - get { return true; } - } - - public override bool CanSeek - { - get { return true; } - } - - public override bool CanWrite - { - get { return false; } - } - - public override void Flush() - { - streamBuffer.SetLength(0); - } - - public override long Length - { - get { return this.length; } - } - private long length; - - public override long Position - { - get { return streamBuffer.Position; } - set { streamBuffer.Position = value; } - } - - public override int Read(byte[] buffer, int offset, int count) - { - // If we don't have enough data to fill this from memory, cache more. - long previousPosition = Position; - bool consumedStream = false; - int totalCount = count; - while((! consumedStream) && - ((Position + totalCount) > streamBuffer.Length)) - { - int bytesToRead = Math.Min(totalCount, copyBuffer.Length); - int bytesRead = baseStream.Read(copyBuffer, 0, bytesToRead); - - if (streamBuffer.Position < streamBuffer.Length) - { - // Win8: 651902 no need to -1 here as Position refers to the place - // where we can start writing from. - streamBuffer.Position = streamBuffer.Length; - } - - streamBuffer.Write(copyBuffer, 0, bytesRead); - - totalCount -= bytesRead; - if (bytesRead < bytesToRead) - { - consumedStream = true; - } - } - - // Reset our backing store to its official position, as reading - // for the CopyTo updates the position. - streamBuffer.Seek(previousPosition, SeekOrigin.Begin); - - // Read from the backing store into the requested buffer. - int read = streamBuffer.Read(buffer, offset, count); - - if (read < count) - { - SetLength(Position); - } - - return read; - } - - public override long Seek(long offset, SeekOrigin origin) - { - return streamBuffer.Seek(offset, origin); - } - - public override void SetLength(long value) - { - this.length = value; - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - } - -} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/PSUserAgent.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/PSUserAgent.cs index e513f1216b..1a6e8111a7 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/PSUserAgent.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/PSUserAgent.cs @@ -1,12 +1,10 @@ -/********************************************************************++ +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System; using System.Management.Automation; using System.Globalization; -using System.Reflection; -using System.Diagnostics; namespace Microsoft.PowerShell.Commands { @@ -132,11 +130,15 @@ namespace Microsoft.PowerShell.Commands { get { +#if CORECLR + return System.Runtime.InteropServices.RuntimeInformation.OSDescription; +#else OperatingSystem os = Environment.OSVersion; string platform = GetOSName(os.Platform); string formattedOS = string.Format(CultureInfo.InvariantCulture, "{0} {1}.{2}", platform, os.Version.Major, os.Version.Minor); return (formattedOS); +#endif } } @@ -148,6 +150,7 @@ namespace Microsoft.PowerShell.Commands } } +#if !CORECLR private static string GetOSName(PlatformID platformId) { string platform; @@ -172,5 +175,6 @@ namespace Microsoft.PowerShell.Commands return (platform); } +#endif } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs index edd1ff6af2..e072ab59e3 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs @@ -1,15 +1,19 @@ -/********************************************************************++ +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System; -using System.Collections.Generic; -using System.Linq; using System.Text; using System.IO; +using System.IO.Compression; using System.Management.Automation; using System.Management.Automation.Internal; + +#if CORECLR +using System.Net.Http; +#else using System.Net; +#endif namespace Microsoft.PowerShell.Commands { @@ -99,44 +103,6 @@ namespace Microsoft.PowerShell.Commands return base.Length; } } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - Initialize(); - return base.BeginRead(buffer, offset, count, callback, state); - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - Initialize(); - return base.BeginWrite(buffer, offset, count, callback, state); - } - - /// - /// - /// - public override void Close() - { - base.Close(); - } /// /// @@ -151,16 +117,6 @@ namespace Microsoft.PowerShell.Commands return base.CopyToAsync(destination, bufferSize, cancellationToken); } - /// - /// - /// - /// - public override byte[] GetBuffer() - { - Initialize(); - return base.GetBuffer(); - } - /// /// /// @@ -264,6 +220,51 @@ namespace Microsoft.PowerShell.Commands base.WriteTo(stream); } + /// + /// + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } + +#if !CORECLR + /// + /// + /// + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + Initialize(); + return base.BeginRead(buffer, offset, count, callback, state); + } + + /// + /// + /// + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + Initialize(); + return base.BeginWrite(buffer, offset, count, callback, state); + } + + /// + /// + /// + public override byte[] GetBuffer() + { + Initialize(); + return base.GetBuffer(); + } + + /// + /// + /// + public override void Close() + { + base.Close(); + } +#endif + /// /// /// @@ -313,7 +314,7 @@ namespace Microsoft.PowerShell.Commands } catch (Exception) { - base.Close(); + base.Dispose(); throw; } } @@ -371,7 +372,7 @@ namespace Microsoft.PowerShell.Commands output.Flush(); } - + internal static void WriteToStream(byte[] input, Stream output) { output.Write(input, 0, input.Length); @@ -468,6 +469,29 @@ namespace Microsoft.PowerShell.Commands return EncodeToBytes(str, null); } +#if CORECLR + internal static Stream GetResponseStream(HttpResponseMessage response) + { + Stream responseStream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult(); + var contentEncoding = response.Content.Headers.ContentEncoding; + + // HttpClient by default will automatically decompress GZip and Deflate content. + // We keep this decompression logic here just in case. + if (contentEncoding != null && contentEncoding.Count > 0) + { + if (contentEncoding.Contains("gzip")) + { + responseStream = new GZipStream(responseStream, CompressionMode.Decompress); + } + else if (contentEncoding.Contains("deflate")) + { + responseStream = new DeflateStream(responseStream, CompressionMode.Decompress); + } + } + + return responseStream; + } +#else internal static Stream GetResponseStream(WebResponse response) { Stream responseStream = response.GetResponseStream(); @@ -478,16 +502,17 @@ namespace Microsoft.PowerShell.Commands { if (contentEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) >= 0) { - responseStream = new System.IO.Compression.GZipStream(responseStream, System.IO.Compression.CompressionMode.Decompress); + responseStream = new GZipStream(responseStream, CompressionMode.Decompress); } else if (contentEncoding.IndexOf("deflate", StringComparison.OrdinalIgnoreCase) >= 0) { - responseStream = new System.IO.Compression.DeflateStream(responseStream, System.IO.Compression.CompressionMode.Decompress); + responseStream = new DeflateStream(responseStream, CompressionMode.Decompress); } } return responseStream; } +#endif #endregion Static Methods } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestMethod.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestMethod.cs index d95a6bf31b..e7781ce30c 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestMethod.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestMethod.cs @@ -2,11 +2,6 @@ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - namespace Microsoft.PowerShell.Commands { /// diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs index a8a87eeeee..38cf7a7e82 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs @@ -1,4 +1,4 @@ -/********************************************************************++ +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ @@ -6,7 +6,6 @@ using System; using System.Net; using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; -using System.Management.Automation; namespace Microsoft.PowerShell.Commands { @@ -21,6 +20,13 @@ namespace Microsoft.PowerShell.Commands [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public Dictionary Headers { get; set; } +#if CORECLR + /// + /// gets or sets the content Headers when using HttpClient + /// + internal Dictionary ContentHeaders { get; set; } +#endif + /// /// gets or sets the Cookies property /// @@ -60,7 +66,7 @@ namespace Microsoft.PowerShell.Commands /// gets or sets the RedirectMax property /// public int MaximumRedirection { get; set; } - + /// /// Construct a new instance of a WebRequestSession object. /// @@ -68,6 +74,9 @@ namespace Microsoft.PowerShell.Commands { // build the headers collection Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); +#if CORECLR + ContentHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); +#endif // build the cookie jar Cookies = new CookieContainer(); diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/map.json b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/map.json new file mode 100644 index 0000000000..917a64958c --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/map.json @@ -0,0 +1,39 @@ +{ + "monad/src/commands/utility/WebCmdlet/ConvertFromJsonCommand.cs": "ConvertFromJsonCommand.cs", + "monad/src/commands/utility/WebCmdlet/ConvertToJsonCommand.cs": "ConvertToJsonCommand.cs", + "monad/src/commands/utility/WebCmdlet/FormObject.cs": "FormObject.cs", + "monad/src/commands/utility/WebCmdlet/FormObjectCollection.cs": "FormObjectCollection.cs", + "monad/src/commands/utility/WebCmdlet/JsonObject.cs": "JsonObject.cs", + "monad/src/commands/utility/WebCmdlet/PSUserAgent.cs": "PSUserAgent.cs", + "monad/src/commands/utility/WebCmdlet/StreamHelper.cs": "StreamHelper.cs", + "monad/src/commands/utility/WebCmdlet/WebCmdletElementCollection.cs": "WebCmdletElementCollection.cs", + "monad/src/commands/utility/WebCmdlet/WebRequestMethod.cs": "WebRequestMethod.cs", + "monad/src/commands/utility/WebCmdlet/WebRequestSession.cs": "WebRequestSession.cs", + "monad/src/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs": "Common/BasicHtmlWebResponseObject.Common.cs", + "monad/src/commands/utility/WebCmdlet/Common/ContentHelper.Common.cs": "Common/ContentHelper.Common.cs", + "monad/src/commands/utility/WebCmdlet/Common/HtmlWebResponseObject.Common.cs": "Common/HtmlWebResponseObject.Common.cs", + "monad/src/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs": "Common/InvokeRestMethodCommand.Common.cs", + "monad/src/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs": "Common/WebRequestPSCmdlet.Common.cs", + "monad/src/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs": "Common/WebResponseObject.Common.cs", + "monad/src/commands/utility/WebCmdlet/CoreCLR/BasicHtmlWebResponseObject.CoreClr.cs": "CoreCLR/BasicHtmlWebResponseObject.CoreClr.cs", + "monad/src/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs": "CoreCLR/ContentHelper.CoreClr.cs", + "monad/src/commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs": "CoreCLR/HtmlWebResponseObject.CoreClr.cs", + "monad/src/commands/utility/WebCmdlet/CoreCLR/HttpKnownHeaderNames.cs": "CoreCLR/HttpKnownHeaderNames.cs", + "monad/src/commands/utility/WebCmdlet/CoreCLR/InvokeRestMethodCommand.CoreClr.cs": "CoreCLR/InvokeRestMethodCommand.CoreClr.cs", + "monad/src/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs": "CoreCLR/InvokeWebRequestCommand.CoreClr.cs", + "monad/src/commands/utility/WebCmdlet/CoreCLR/WebProxy.cs": "CoreCLR/WebProxy.cs", + "monad/src/commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs": "CoreCLR/WebRequestPSCmdlet.CoreClr.cs", + "monad/src/commands/utility/WebCmdlet/CoreCLR/WebResponseHelper.CoreClr.cs": "CoreCLR/WebResponseHelper.CoreClr.cs", + "monad/src/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs": "CoreCLR/WebResponseObject.CoreClr.cs", + "monad/src/commands/utility/WebCmdlet/CoreCLR/WebResponseObjectFactory.CoreClr.cs": "CoreCLR/WebResponseObjectFactory.CoreClr.cs", + "monad/src/commands/utility/WebCmdlet/FullClr/BasicHtmlWebResponseObject.FullClr.cs": "FullClr/BasicHtmlWebResponseObject.FullClr.cs", + "monad/src/commands/utility/WebCmdlet/FullClr/ContentHelper.FullClr.cs": "FullClr/ContentHelper.FullClr.cs", + "monad/src/commands/utility/WebCmdlet/FullClr/HtmlWebResponseObject.FullClr.cs": "FullClr/HtmlWebResponseObject.FullClr.cs", + "monad/src/commands/utility/WebCmdlet/FullClr/InvokeRestMethodCommand.FullClr.cs": "FullClr/InvokeRestMethodCommand.FullClr.cs", + "monad/src/commands/utility/WebCmdlet/FullClr/InvokeWebRequestCommand.FullClr.cs": "FullClr/InvokeWebRequestCommand.FullClr.cs", + "monad/src/commands/utility/WebCmdlet/FullClr/JsonObjectTypeResolver.cs": "FullClr/JsonObjectTypeResolver.cs", + "monad/src/commands/utility/WebCmdlet/FullClr/WebRequestPSCmdlet.FullClr.cs": "FullClr/WebRequestPSCmdlet.FullClr.cs", + "monad/src/commands/utility/WebCmdlet/FullClr/WebResponseHelper.FullClr.cs": "FullClr/WebResponseHelper.FullClr.cs", + "monad/src/commands/utility/WebCmdlet/FullClr/WebResponseObject.FullClr.cs": "FullClr/WebResponseObject.FullClr.cs", + "monad/src/commands/utility/WebCmdlet/FullClr/WebResponseObjectFactory.FullClr.cs": "FullClr/WebResponseObjectFactory.FullClr.cs" +} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteConsoleCmdlet.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteConsoleCmdlet.cs index 1b9283221d..23a7754fed 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteConsoleCmdlet.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteConsoleCmdlet.cs @@ -170,11 +170,6 @@ namespace Microsoft.PowerShell.Commands // Expected if the host is not interactive, or doesn't have Foreground / Background // colours. } - catch (System.PlatformNotSupportedException) - { - // TODO:PSL Support setting these colors. Expected - // exception on non-windows CoreCLR for now. - } this.WriteInformation(informationMessage, new string[] { "PSHOST" }); this.Host.UI.TranscribeResult(result); diff --git a/src/Microsoft.PowerShell.Commands.Utility/map.json b/src/Microsoft.PowerShell.Commands.Utility/map.json index 0843595f04..eac3a5593a 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/map.json +++ b/src/Microsoft.PowerShell.Commands.Utility/map.json @@ -1,157 +1,137 @@ { - "monad/src/commands/utility/resources/TraceCommandStrings.resx": "resources/TraceCommandStrings.resx", - "monad/src/commands/utility/WebCmdlet/StreamHelper.cs": "commands/utility/WebCmdlet/StreamHelper.cs", - "monad/src/commands/utility/GetDateCommand.cs": "commands/utility/GetDateCommand.cs", - "monad/src/commands/utility/resources/HostStrings.resx": "resources/HostStrings.resx", - "monad/src/commands/utility/ShowCommand/ShowCommandCommandInfo.cs": "commands/utility/ShowCommand/ShowCommandCommandInfo.cs", - "monad/src/commands/utility/resources/UtilityCommonStrings.resx": "resources/UtilityCommonStrings.resx", - "monad/src/commands/utility/WebCmdlet/WebResponseObject.cs": "commands/utility/WebCmdlet/WebResponseObject.cs", - "monad/src/commands/utility/resources/ConvertStringResources.resx": "resources/ConvertStringResources.resx", - "monad/src/commands/utility/SetDateCommand.cs": "commands/utility/SetDateCommand.cs", - "monad/src/commands/utility/Get-PSBreakpoint.cs": "commands/utility/Get-PSBreakpoint.cs", - "monad/src/commands/utility/WebCmdlet/InvokeRestMethodCommand.cs": "commands/utility/WebCmdlet/InvokeRestMethodCommand.cs", - "monad/src/commands/utility/WebCmdlet/WebResponseObjectFactory.cs": "commands/utility/WebCmdlet/WebResponseObjectFactory.cs", - "monad/src/commands/utility/InvokeCommandCmdlet.cs": "commands/utility/InvokeCommandCmdlet.cs", - "monad/src/commands/utility/FormatAndOutput/common/GetFormatDataCommand.cs": "commands/utility/FormatAndOutput/common/GetFormatDataCommand.cs", - "monad/src/commands/utility/ConvertFrom-StringData.cs": "commands/utility/ConvertFrom-StringData.cs", - "monad/src/commands/utility/WebCmdlet/InvokeWebRequestCommand.cs": "commands/utility/WebCmdlet/InvokeWebRequestCommand.cs", - "monad/src/commands/utility/resources/ImplicitRemotingStrings.resx": "resources/ImplicitRemotingStrings.resx", - "monad/src/commands/utility/OrderObjectBase.cs": "commands/utility/OrderObjectBase.cs", - "monad/src/commands/utility/GetEventCommand.cs": "commands/utility/GetEventCommand.cs", - "monad/src/commands/utility/resources/NewObjectStrings.resx": "resources/NewObjectStrings.resx", - "monad/src/commands/utility/resources/Debugger.resx": "resources/Debugger.resx", - "monad/src/commands/utility/WebCmdlet/FormObjectCollection.cs": "commands/utility/WebCmdlet/FormObjectCollection.cs", - "monad/src/commands/utility/trace/GetTracerCommand.cs": "commands/utility/trace/GetTracerCommand.cs", - "monad/src/commands/utility/resources/MatchStringStrings.resx": "resources/MatchStringStrings.resx", - "monad/src/commands/utility/UnblockFile.cs": "commands/utility/UnblockFile.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/ColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/ColumnInfo.cs", - "monad/src/commands/utility/resources/FlashExtractStrings.resx": "resources/FlashExtractStrings.resx", - "monad/src/commands/utility/resources/ConvertFromStringResources.resx": "resources/ConvertFromStringResources.resx", - "monad/src/commands/utility/Send-MailMessage.cs": "commands/utility/Send-MailMessage.cs", - "monad/src/commands/utility/WebCmdlet/WebRequestPSCmdlet.cs": "commands/utility/WebCmdlet/WebRequestPSCmdlet.cs", - "monad/src/commands/utility/GetHostCmdlet.cs": "commands/utility/GetHostCmdlet.cs", - "monad/src/commands/utility/WebCmdlet/WebResponseHelper.cs": "commands/utility/WebCmdlet/WebResponseHelper.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs": "commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs", - "monad/src/commands/utility/WriteProgressCmdlet.cs": "commands/utility/WriteProgressCmdlet.cs", - "monad/src/commands/utility/ShowCommand/ShowCommandModuleInfo.cs": "commands/utility/ShowCommand/ShowCommandModuleInfo.cs", - "monad/src/commands/utility/FormatAndOutput/format-table/Format-Table.cs": "commands/utility/FormatAndOutput/format-table/Format-Table.cs", - "monad/src/commands/utility/resources/UpdateListStrings.resx": "resources/UpdateListStrings.resx", - "monad/src/commands/utility/WebCmdlet/WebCmdletElementCollection.cs": "commands/utility/WebCmdlet/WebCmdletElementCollection.cs", - "monad/src/commands/utility/GetRunspaceCommand.cs": "commands/utility/GetRunspaceCommand.cs", - "monad/src/commands/utility/resources/SelectObjectStrings.resx": "resources/SelectObjectStrings.resx", - "monad/src/commands/utility/FormatAndOutput/out-string/out-string.cs": "commands/utility/FormatAndOutput/out-string/out-string.cs", - "monad/src/commands/utility/MatchString.cs": "commands/utility/MatchString.cs", - "monad/src/commands/utility/trace/TraceExpressionCommand.cs": "commands/utility/trace/TraceExpressionCommand.cs", - "monad/src/commands/utility/resources/ImmutableStrings.resx": "resources/ImmutableStrings.resx", - "monad/src/commands/utility/RegisterObjectEventCommand.cs": "commands/utility/RegisterObjectEventCommand.cs", - "monad/src/commands/utility/Set-PSBreakpoint.cs": "commands/utility/Set-PSBreakpoint.cs", - "monad/src/commands/utility/trace/TraceCommandBase.cs": "commands/utility/trace/TraceCommandBase.cs", - "monad/src/commands/utility/resources/MeasureObjectStrings.resx": "resources/MeasureObjectStrings.resx", - "monad/src/commands/utility/ConsoleColorCmdlet.cs": "commands/utility/ConsoleColorCmdlet.cs", - "monad/src/commands/utility/CSVCommands.cs": "commands/utility/CSVCommands.cs", - "monad/src/commands/utility/GetUICultureCommand.cs": "commands/utility/GetUICultureCommand.cs", - "monad/src/commands/utility/tee-object.cs": "commands/utility/tee-object.cs", - "monad/src/commands/utility/resources/SendMailMessageStrings.resx": "resources/SendMailMessageStrings.resx", - "monad/src/commands/utility/WriteConsoleCmdlet.cs": "commands/utility/WriteConsoleCmdlet.cs", - "monad/src/commands/utility/trace/TraceListenerCommandBase.cs": "commands/utility/trace/TraceListenerCommandBase.cs", - "monad/src/commands/utility/FormatAndOutput/common/WriteFormatDataCommand.cs": "commands/utility/FormatAndOutput/common/WriteFormatDataCommand.cs", - "monad/src/commands/utility/ShowCommand/ShowCommandParameterType.cs": "commands/utility/ShowCommand/ShowCommandParameterType.cs", - "monad/src/commands/utility/TimeExpressionCommand.cs": "commands/utility/TimeExpressionCommand.cs", - "monad/src/commands/utility/resources/ConvertFromStringData.resx": "resources/ConvertFromStringData.resx", - "monad/src/commands/utility/resources/GetRandomCommandStrings.resx": "resources/GetRandomCommandStrings.resx", - "monad/src/commands/utility/WaitEventCommand.cs": "commands/utility/WaitEventCommand.cs", - "monad/src/commands/utility/XmlCommands.cs": "commands/utility/XmlCommands.cs", - "monad/src/commands/utility/UtilityCommon.cs": "commands/utility/UtilityCommon.cs", - "monad/src/commands/utility/FormatAndOutput/out-file/Out-File.cs": "commands/utility/FormatAndOutput/out-file/Out-File.cs", - "monad/src/commands/utility/WebCmdlet/ConvertFromJsonCommand.cs": "commands/utility/WebCmdlet/ConvertFromJsonCommand.cs", - "monad/src/commands/utility/RemoveEventCommand.cs": "commands/utility/RemoveEventCommand.cs", - "monad/src/commands/utility/ReadConsoleCmdlet.cs": "commands/utility/ReadConsoleCmdlet.cs", - "monad/src/commands/utility/Measure-Object.cs": "commands/utility/Measure-Object.cs", - "monad/src/commands/utility/Disable-PSBreakpoint.cs": "commands/utility/Disable-PSBreakpoint.cs", - "monad/src/commands/utility/ShowCommand/ShowCommandParameterSetInfo.cs": "commands/utility/ShowCommand/ShowCommandParameterSetInfo.cs", - "monad/src/commands/utility/resources/OutPrinterDisplayStrings.resx": "resources/OutPrinterDisplayStrings.resx", - "monad/src/commands/utility/CustomSerialization.cs": "commands/utility/CustomSerialization.cs", - "monad/src/commands/utility/NewAliasCommand.cs": "commands/utility/NewAliasCommand.cs", - "monad/src/commands/utility/CustomSerializationStrings.cs": "commands/utility/CustomSerializationStrings.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/HeaderInfo.cs": "commands/utility/FormatAndOutput/OutGridView/HeaderInfo.cs", - "monad/src/commands/utility/WebCmdlet/HtmlWebResponseObject.cs": "commands/utility/WebCmdlet/HtmlWebResponseObject.cs", - "monad/src/commands/utility/WebCmdlet/ContentHelper.cs": "commands/utility/WebCmdlet/ContentHelper.cs", - "monad/src/commands/utility/FormatAndOutput/format-object/format-object.cs": "commands/utility/FormatAndOutput/format-object/format-object.cs", - "monad/src/commands/utility/StartSleepCommand.cs": "commands/utility/StartSleepCommand.cs", - "monad/src/commands/utility/WriteAliasCommandBase.cs": "commands/utility/WriteAliasCommandBase.cs", - "monad/src/commands/utility/Write-Object.cs": "commands/utility/Write-Object.cs", - "monad/src/commands/utility/UnregisterEventCommand.cs": "commands/utility/UnregisterEventCommand.cs", - "monad/src/commands/utility/FormatAndOutput/format-list/Format-List.cs": "commands/utility/FormatAndOutput/format-list/Format-List.cs", - "monad/src/commands/utility/GetUnique.cs": "commands/utility/GetUnique.cs", - "monad/src/commands/utility/ShowCommand/ShowCommandProxy.cs": "commands/utility/ShowCommand/ShowCommandProxy.cs", - "monad/src/commands/utility/trace/MshHostTraceListener.cs": "commands/utility/trace/MshHostTraceListener.cs", - "monad/src/commands/utility/Import-LocalizedData.cs": "commands/utility/Import-LocalizedData.cs", - "monad/src/commands/utility/WebCmdlet/PSUserAgent.cs": "commands/utility/WebCmdlet/PSUserAgent.cs", - "monad/src/commands/utility/AddType.cs": "commands/utility/AddType.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/ScalarTypeColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/ScalarTypeColumnInfo.cs", - "monad/src/commands/utility/GetAliasCommand.cs": "commands/utility/GetAliasCommand.cs", - "monad/src/commands/utility/resources/AddTypeStrings.resx": "resources/AddTypeStrings.resx", - "monad/src/commands/utility/GetEventSubscriberCommand.cs": "commands/utility/GetEventSubscriberCommand.cs", - "monad/src/commands/utility/resources/CsvCommandStrings.resx": "resources/CsvCommandStrings.resx", - "monad/src/commands/utility/resources/UtilityMshSnapinResources.resx": "resources/UtilityMshSnapinResources.resx", - "monad/src/commands/utility/resources/EventingStrings.resx": "resources/EventingStrings.resx", - "monad/src/commands/utility/trace/SetTracerCommand.cs": "commands/utility/trace/SetTracerCommand.cs", - "monad/src/commands/utility/select-object.cs": "commands/utility/select-object.cs", - "monad/src/commands/utility/resources/WriteErrorStrings.resx": "resources/WriteErrorStrings.resx", - "monad/src/commands/utility/Get-PSCallStack.cs": "commands/utility/Get-PSCallStack.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/TableView.cs": "commands/utility/FormatAndOutput/OutGridView/TableView.cs", - "monad/src/commands/utility/NewTimeSpanCommand.cs": "commands/utility/NewTimeSpanCommand.cs", - "monad/src/commands/utility/resources/HttpCommandStrings.resx": "resources/HttpCommandStrings.resx", - "monad/src/commands/utility/ImplicitRemotingCommands.cs": "commands/utility/ImplicitRemotingCommands.cs", - "monad/src/commands/utility/resources/SortObjectStrings.resx": "resources/SortObjectStrings.resx", - "monad/src/commands/utility/resources/AddMember.resx": "resources/AddMember.resx", - "monad/src/commands/utility/neweventcommand.cs": "commands/utility/neweventcommand.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/OutWindowProxy.cs": "commands/utility/FormatAndOutput/OutGridView/OutWindowProxy.cs", - "monad/src/commands/utility/FormatAndOutput/out-printer/PrinterLineOutput.cs": "commands/utility/FormatAndOutput/out-printer/PrinterLineOutput.cs", - "monad/src/commands/utility/resources/WebCmdletStrings.resx": "resources/WebCmdletStrings.resx", - "monad/src/commands/utility/write.cs": "commands/utility/write.cs", - "monad/src/commands/utility/WebCmdlet/BasicHtmlWebResponseObject.cs": "commands/utility/WebCmdlet/BasicHtmlWebResponseObject.cs", - "monad/src/commands/utility/convert-HTML.cs": "commands/utility/convert-HTML.cs", - "monad/src/commands/utility/RegisterPSEventCommand.cs": "commands/utility/RegisterPSEventCommand.cs", - "monad/src/commands/utility/ShowCommand/ShowCommand.cs": "commands/utility/ShowCommand/ShowCommand.cs", - "monad/src/commands/utility/ShowCommand/ShowCommandParameterInfo.cs": "commands/utility/ShowCommand/ShowCommandParameterInfo.cs", - "monad/src/commands/utility/SetAliasCommand.cs": "commands/utility/SetAliasCommand.cs", - "monad/src/commands/utility/resources/ImportLocalizedDataStrings.resx": "resources/ImportLocalizedDataStrings.resx", - "monad/src/commands/utility/EnableDisableRunspaceDebugCommand.cs": "commands/utility/EnableDisableRunspaceDebugCommand.cs", - "monad/src/commands/utility/group-object.cs": "commands/utility/group-object.cs", - "monad/src/commands/utility/GetMember.cs": "commands/utility/GetMember.cs", - "monad/src/singleshell/installer/MshUtilityMshSnapin.cs": "singleshell/installer/MshUtilityMshSnapin.cs", - "monad/src/commands/utility/ImportAliasCommand.cs": "commands/utility/ImportAliasCommand.cs", - "monad/src/commands/utility/Update-Data.cs": "commands/utility/Update-Data.cs", - "monad/src/commands/utility/resources/WriteProgressResourceStrings.resx": "resources/WriteProgressResourceStrings.resx", - "monad/src/commands/utility/sort-object.cs": "commands/utility/sort-object.cs", - "monad/src/commands/utility/WebCmdlet/JsonObject.cs": "commands/utility/WebCmdlet/JsonObject.cs", "monad/src/commands/utility/AddMember.cs": "commands/utility/AddMember.cs", + "monad/src/commands/utility/AddType.cs": "commands/utility/AddType.cs", "monad/src/commands/utility/compare-object.cs": "commands/utility/compare-object.cs", - "monad/src/commands/utility/Update-TypeData.cs": "commands/utility/Update-TypeData.cs", - "monad/src/commands/utility/FormatAndOutput/out-printer/out-printer.cs": "commands/utility/FormatAndOutput/out-printer/out-printer.cs", - "monad/src/commands/utility/update-list.cs": "commands/utility/update-list.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/OriginalColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/OriginalColumnInfo.cs", - "monad/src/commands/utility/GetCultureCommand.cs": "commands/utility/GetCultureCommand.cs", - "monad/src/commands/utility/resources/GetMember.resx": "resources/GetMember.resx", - "monad/src/commands/utility/WebCmdlet/ConvertToJsonCommand.cs": "commands/utility/WebCmdlet/ConvertToJsonCommand.cs", - "monad/src/commands/utility/ObjectCommandComparer.cs": "commands/utility/ObjectCommandComparer.cs", - "monad/src/commands/utility/resources/AliasCommandStrings.resx": "resources/AliasCommandStrings.resx", - "monad/src/commands/utility/WebCmdlet/FormObject.cs": "commands/utility/WebCmdlet/FormObject.cs", - "monad/src/commands/utility/FormatAndOutput/format-wide/Format-Wide.cs": "commands/utility/FormatAndOutput/format-wide/Format-Wide.cs", - "monad/src/commands/utility/resources/FormatAndOut_out_gridview.resx": "resources/FormatAndOut_out_gridview.resx", - "monad/src/commands/utility/WebCmdlet/WebRequestSession.cs": "commands/utility/WebCmdlet/WebRequestSession.cs", - "monad/src/commands/utility/WebCmdlet/JsonObjectTypeResolver.cs": "commands/utility/WebCmdlet/JsonObjectTypeResolver.cs", - "monad/src/commands/utility/FormatAndOutput/OutGridView/ExpressionColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/ExpressionColumnInfo.cs", - "monad/src/commands/utility/Remove-PSBreakpoint.cs": "commands/utility/Remove-PSBreakpoint.cs", - "monad/src/commands/utility/Var.cs": "commands/utility/Var.cs", - "monad/src/commands/utility/resources/VariableCommandStrings.resx": "resources/VariableCommandStrings.resx", - "monad/src/commands/utility/new-object.cs": "commands/utility/new-object.cs", - "monad/src/commands/utility/GetRandomCommand.cs": "commands/utility/GetRandomCommand.cs", + "monad/src/commands/utility/ConsoleColorCmdlet.cs": "commands/utility/ConsoleColorCmdlet.cs", + "monad/src/commands/utility/convert-HTML.cs": "commands/utility/convert-HTML.cs", + "monad/src/commands/utility/ConvertFrom-StringData.cs": "commands/utility/ConvertFrom-StringData.cs", "monad/src/commands/utility/Csv.cs": "commands/utility/Csv.cs", - "monad/src/commands/utility/resources/UpdateDataStrings.resx": "resources/UpdateDataStrings.resx", - "monad/src/commands/utility/ExportAliasCommand.cs": "commands/utility/ExportAliasCommand.cs", + "monad/src/commands/utility/CSVCommands.cs": "commands/utility/CSVCommands.cs", + "monad/src/commands/utility/CustomSerialization.cs": "commands/utility/CustomSerialization.cs", + "monad/src/commands/utility/CustomSerializationStrings.cs": "commands/utility/CustomSerializationStrings.cs", "monad/src/commands/utility/DebugRunspaceCommand.cs": "commands/utility/DebugRunspaceCommand.cs", + "monad/src/commands/utility/Disable-PSBreakpoint.cs": "commands/utility/Disable-PSBreakpoint.cs", "monad/src/commands/utility/Enable-PSBreakpoint.cs": "commands/utility/Enable-PSBreakpoint.cs", - "monad/src/commands/utility/WebCmdlet/WebRequestMethod.cs": "commands/utility/WebCmdlet/WebRequestMethod.cs" + "monad/src/commands/utility/EnableDisableRunspaceDebugCommand.cs": "commands/utility/EnableDisableRunspaceDebugCommand.cs", + "monad/src/commands/utility/ExportAliasCommand.cs": "commands/utility/ExportAliasCommand.cs", + "monad/src/commands/utility/FormatAndOutput/common/GetFormatDataCommand.cs": "commands/utility/FormatAndOutput/common/GetFormatDataCommand.cs", + "monad/src/commands/utility/FormatAndOutput/common/WriteFormatDataCommand.cs": "commands/utility/FormatAndOutput/common/WriteFormatDataCommand.cs", + "monad/src/commands/utility/FormatAndOutput/format-list/Format-List.cs": "commands/utility/FormatAndOutput/format-list/Format-List.cs", + "monad/src/commands/utility/FormatAndOutput/format-object/format-object.cs": "commands/utility/FormatAndOutput/format-object/format-object.cs", + "monad/src/commands/utility/FormatAndOutput/format-table/Format-Table.cs": "commands/utility/FormatAndOutput/format-table/Format-Table.cs", + "monad/src/commands/utility/FormatAndOutput/format-wide/Format-Wide.cs": "commands/utility/FormatAndOutput/format-wide/Format-Wide.cs", + "monad/src/commands/utility/FormatAndOutput/out-file/Out-File.cs": "commands/utility/FormatAndOutput/out-file/Out-File.cs", + "monad/src/commands/utility/FormatAndOutput/out-printer/out-printer.cs": "commands/utility/FormatAndOutput/out-printer/out-printer.cs", + "monad/src/commands/utility/FormatAndOutput/out-printer/PrinterLineOutput.cs": "commands/utility/FormatAndOutput/out-printer/PrinterLineOutput.cs", + "monad/src/commands/utility/FormatAndOutput/out-string/out-string.cs": "commands/utility/FormatAndOutput/out-string/out-string.cs", + "monad/src/commands/utility/FormatAndOutput/OutGridView/ColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/ColumnInfo.cs", + "monad/src/commands/utility/FormatAndOutput/OutGridView/ExpressionColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/ExpressionColumnInfo.cs", + "monad/src/commands/utility/FormatAndOutput/OutGridView/HeaderInfo.cs": "commands/utility/FormatAndOutput/OutGridView/HeaderInfo.cs", + "monad/src/commands/utility/FormatAndOutput/OutGridView/OriginalColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/OriginalColumnInfo.cs", + "monad/src/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs": "commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs", + "monad/src/commands/utility/FormatAndOutput/OutGridView/OutWindowProxy.cs": "commands/utility/FormatAndOutput/OutGridView/OutWindowProxy.cs", + "monad/src/commands/utility/FormatAndOutput/OutGridView/ScalarTypeColumnInfo.cs": "commands/utility/FormatAndOutput/OutGridView/ScalarTypeColumnInfo.cs", + "monad/src/commands/utility/FormatAndOutput/OutGridView/TableView.cs": "commands/utility/FormatAndOutput/OutGridView/TableView.cs", + "monad/src/commands/utility/Get-PSBreakpoint.cs": "commands/utility/Get-PSBreakpoint.cs", + "monad/src/commands/utility/Get-PSCallStack.cs": "commands/utility/Get-PSCallStack.cs", + "monad/src/commands/utility/GetAliasCommand.cs": "commands/utility/GetAliasCommand.cs", + "monad/src/commands/utility/GetCultureCommand.cs": "commands/utility/GetCultureCommand.cs", + "monad/src/commands/utility/GetDateCommand.cs": "commands/utility/GetDateCommand.cs", + "monad/src/commands/utility/GetEventCommand.cs": "commands/utility/GetEventCommand.cs", + "monad/src/commands/utility/GetEventSubscriberCommand.cs": "commands/utility/GetEventSubscriberCommand.cs", + "monad/src/commands/utility/GetHostCmdlet.cs": "commands/utility/GetHostCmdlet.cs", + "monad/src/commands/utility/GetMember.cs": "commands/utility/GetMember.cs", + "monad/src/commands/utility/GetRandomCommand.cs": "commands/utility/GetRandomCommand.cs", + "monad/src/commands/utility/GetRunspaceCommand.cs": "commands/utility/GetRunspaceCommand.cs", + "monad/src/commands/utility/GetUICultureCommand.cs": "commands/utility/GetUICultureCommand.cs", + "monad/src/commands/utility/GetUnique.cs": "commands/utility/GetUnique.cs", + "monad/src/commands/utility/group-object.cs": "commands/utility/group-object.cs", + "monad/src/commands/utility/ImplicitRemotingCommands.cs": "commands/utility/ImplicitRemotingCommands.cs", + "monad/src/commands/utility/Import-LocalizedData.cs": "commands/utility/Import-LocalizedData.cs", + "monad/src/commands/utility/ImportAliasCommand.cs": "commands/utility/ImportAliasCommand.cs", + "monad/src/commands/utility/InvokeCommandCmdlet.cs": "commands/utility/InvokeCommandCmdlet.cs", + "monad/src/commands/utility/MatchString.cs": "commands/utility/MatchString.cs", + "monad/src/commands/utility/Measure-Object.cs": "commands/utility/Measure-Object.cs", + "monad/src/commands/utility/new-object.cs": "commands/utility/new-object.cs", + "monad/src/commands/utility/NewAliasCommand.cs": "commands/utility/NewAliasCommand.cs", + "monad/src/commands/utility/neweventcommand.cs": "commands/utility/neweventcommand.cs", + "monad/src/commands/utility/NewTimeSpanCommand.cs": "commands/utility/NewTimeSpanCommand.cs", + "monad/src/commands/utility/ObjectCommandComparer.cs": "commands/utility/ObjectCommandComparer.cs", + "monad/src/commands/utility/OrderObjectBase.cs": "commands/utility/OrderObjectBase.cs", + "monad/src/commands/utility/ReadConsoleCmdlet.cs": "commands/utility/ReadConsoleCmdlet.cs", + "monad/src/commands/utility/RegisterObjectEventCommand.cs": "commands/utility/RegisterObjectEventCommand.cs", + "monad/src/commands/utility/RegisterPSEventCommand.cs": "commands/utility/RegisterPSEventCommand.cs", + "monad/src/commands/utility/Remove-PSBreakpoint.cs": "commands/utility/Remove-PSBreakpoint.cs", + "monad/src/commands/utility/RemoveEventCommand.cs": "commands/utility/RemoveEventCommand.cs", + "monad/src/commands/utility/resources/AddMember.resx": "resources/AddMember.resx", + "monad/src/commands/utility/resources/AddTypeStrings.resx": "resources/AddTypeStrings.resx", + "monad/src/commands/utility/resources/AliasCommandStrings.resx": "resources/AliasCommandStrings.resx", + "monad/src/commands/utility/resources/ConvertFromStringData.resx": "resources/ConvertFromStringData.resx", + "monad/src/commands/utility/resources/ConvertFromStringResources.resx": "resources/ConvertFromStringResources.resx", + "monad/src/commands/utility/resources/ConvertStringResources.resx": "resources/ConvertStringResources.resx", + "monad/src/commands/utility/resources/CsvCommandStrings.resx": "resources/CsvCommandStrings.resx", + "monad/src/commands/utility/resources/Debugger.resx": "resources/Debugger.resx", + "monad/src/commands/utility/resources/EventingStrings.resx": "resources/EventingStrings.resx", + "monad/src/commands/utility/resources/FlashExtractStrings.resx": "resources/FlashExtractStrings.resx", + "monad/src/commands/utility/resources/FormatAndOut_out_gridview.resx": "resources/FormatAndOut_out_gridview.resx", + "monad/src/commands/utility/resources/GetMember.resx": "resources/GetMember.resx", + "monad/src/commands/utility/resources/GetRandomCommandStrings.resx": "resources/GetRandomCommandStrings.resx", + "monad/src/commands/utility/resources/HostStrings.resx": "resources/HostStrings.resx", + "monad/src/commands/utility/resources/HttpCommandStrings.resx": "resources/HttpCommandStrings.resx", + "monad/src/commands/utility/resources/ImmutableStrings.resx": "resources/ImmutableStrings.resx", + "monad/src/commands/utility/resources/ImplicitRemotingStrings.resx": "resources/ImplicitRemotingStrings.resx", + "monad/src/commands/utility/resources/ImportLocalizedDataStrings.resx": "resources/ImportLocalizedDataStrings.resx", + "monad/src/commands/utility/resources/MatchStringStrings.resx": "resources/MatchStringStrings.resx", + "monad/src/commands/utility/resources/MeasureObjectStrings.resx": "resources/MeasureObjectStrings.resx", + "monad/src/commands/utility/resources/NewObjectStrings.resx": "resources/NewObjectStrings.resx", + "monad/src/commands/utility/resources/OutPrinterDisplayStrings.resx": "resources/OutPrinterDisplayStrings.resx", + "monad/src/commands/utility/resources/SelectObjectStrings.resx": "resources/SelectObjectStrings.resx", + "monad/src/commands/utility/resources/SendMailMessageStrings.resx": "resources/SendMailMessageStrings.resx", + "monad/src/commands/utility/resources/SortObjectStrings.resx": "resources/SortObjectStrings.resx", + "monad/src/commands/utility/resources/TraceCommandStrings.resx": "resources/TraceCommandStrings.resx", + "monad/src/commands/utility/resources/UpdateDataStrings.resx": "resources/UpdateDataStrings.resx", + "monad/src/commands/utility/resources/UpdateListStrings.resx": "resources/UpdateListStrings.resx", + "monad/src/commands/utility/resources/UtilityCommonStrings.resx": "resources/UtilityCommonStrings.resx", + "monad/src/commands/utility/resources/UtilityMshSnapinResources.resx": "resources/UtilityMshSnapinResources.resx", + "monad/src/commands/utility/resources/VariableCommandStrings.resx": "resources/VariableCommandStrings.resx", + "monad/src/commands/utility/resources/WebCmdletStrings.resx": "resources/WebCmdletStrings.resx", + "monad/src/commands/utility/resources/WriteErrorStrings.resx": "resources/WriteErrorStrings.resx", + "monad/src/commands/utility/resources/WriteProgressResourceStrings.resx": "resources/WriteProgressResourceStrings.resx", + "monad/src/commands/utility/select-object.cs": "commands/utility/select-object.cs", + "monad/src/commands/utility/Send-MailMessage.cs": "commands/utility/Send-MailMessage.cs", + "monad/src/commands/utility/Set-PSBreakpoint.cs": "commands/utility/Set-PSBreakpoint.cs", + "monad/src/commands/utility/SetAliasCommand.cs": "commands/utility/SetAliasCommand.cs", + "monad/src/commands/utility/SetDateCommand.cs": "commands/utility/SetDateCommand.cs", + "monad/src/commands/utility/ShowCommand/ShowCommand.cs": "commands/utility/ShowCommand/ShowCommand.cs", + "monad/src/commands/utility/ShowCommand/ShowCommandCommandInfo.cs": "commands/utility/ShowCommand/ShowCommandCommandInfo.cs", + "monad/src/commands/utility/ShowCommand/ShowCommandModuleInfo.cs": "commands/utility/ShowCommand/ShowCommandModuleInfo.cs", + "monad/src/commands/utility/ShowCommand/ShowCommandParameterInfo.cs": "commands/utility/ShowCommand/ShowCommandParameterInfo.cs", + "monad/src/commands/utility/ShowCommand/ShowCommandParameterSetInfo.cs": "commands/utility/ShowCommand/ShowCommandParameterSetInfo.cs", + "monad/src/commands/utility/ShowCommand/ShowCommandParameterType.cs": "commands/utility/ShowCommand/ShowCommandParameterType.cs", + "monad/src/commands/utility/ShowCommand/ShowCommandProxy.cs": "commands/utility/ShowCommand/ShowCommandProxy.cs", + "monad/src/commands/utility/sort-object.cs": "commands/utility/sort-object.cs", + "monad/src/commands/utility/StartSleepCommand.cs": "commands/utility/StartSleepCommand.cs", + "monad/src/commands/utility/tee-object.cs": "commands/utility/tee-object.cs", + "monad/src/commands/utility/TimeExpressionCommand.cs": "commands/utility/TimeExpressionCommand.cs", + "monad/src/commands/utility/trace/GetTracerCommand.cs": "commands/utility/trace/GetTracerCommand.cs", + "monad/src/commands/utility/trace/MshHostTraceListener.cs": "commands/utility/trace/MshHostTraceListener.cs", + "monad/src/commands/utility/trace/SetTracerCommand.cs": "commands/utility/trace/SetTracerCommand.cs", + "monad/src/commands/utility/trace/TraceCommandBase.cs": "commands/utility/trace/TraceCommandBase.cs", + "monad/src/commands/utility/trace/TraceExpressionCommand.cs": "commands/utility/trace/TraceExpressionCommand.cs", + "monad/src/commands/utility/trace/TraceListenerCommandBase.cs": "commands/utility/trace/TraceListenerCommandBase.cs", + "monad/src/commands/utility/UnblockFile.cs": "commands/utility/UnblockFile.cs", + "monad/src/commands/utility/UnregisterEventCommand.cs": "commands/utility/UnregisterEventCommand.cs", + "monad/src/commands/utility/Update-Data.cs": "commands/utility/Update-Data.cs", + "monad/src/commands/utility/update-list.cs": "commands/utility/update-list.cs", + "monad/src/commands/utility/Update-TypeData.cs": "commands/utility/Update-TypeData.cs", + "monad/src/commands/utility/UtilityCommon.cs": "commands/utility/UtilityCommon.cs", + "monad/src/commands/utility/Var.cs": "commands/utility/Var.cs", + "monad/src/commands/utility/WaitEventCommand.cs": "commands/utility/WaitEventCommand.cs", + "monad/src/commands/utility/Write-Object.cs": "commands/utility/Write-Object.cs", + "monad/src/commands/utility/write.cs": "commands/utility/write.cs", + "monad/src/commands/utility/WriteAliasCommandBase.cs": "commands/utility/WriteAliasCommandBase.cs", + "monad/src/commands/utility/WriteConsoleCmdlet.cs": "commands/utility/WriteConsoleCmdlet.cs", + "monad/src/commands/utility/WriteProgressCmdlet.cs": "commands/utility/WriteProgressCmdlet.cs", + "monad/src/commands/utility/XmlCommands.cs": "commands/utility/XmlCommands.cs", + "monad/src/singleshell/installer/MshUtilityMshSnapin.cs": "singleshell/installer/MshUtilityMshSnapin.cs", } diff --git a/src/Microsoft.PowerShell.Commands.Utility/project.json b/src/Microsoft.PowerShell.Commands.Utility/project.json index e03f3819d0..af085ef54c 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/project.json +++ b/src/Microsoft.PowerShell.Commands.Utility/project.json @@ -40,29 +40,23 @@ "commands/utility/ShowCommand/ShowCommandParameterInfo.cs", "commands/utility/ShowCommand/ShowCommandParameterSetInfo.cs", "commands/utility/ShowCommand/ShowCommandParameterType.cs", - "commands/utility/ShowCommand/ShowCommandProxy.cs", - "commands/utility/WebCmdlet/BasicHtmlWebResponseObject.cs", - "commands/utility/WebCmdlet/ContentHelper.cs", - "commands/utility/WebCmdlet/FormObject.cs", - "commands/utility/WebCmdlet/FormObjectCollection.cs", - "commands/utility/WebCmdlet/HtmlWebResponseObject.cs", - "commands/utility/WebCmdlet/InvokeRestMethodCommand.cs", - "commands/utility/WebCmdlet/InvokeWebRequestCommand.cs", - "commands/utility/WebCmdlet/JsonObjectTypeResolver.cs", - "commands/utility/WebCmdlet/PSUserAgent.cs", - "commands/utility/WebCmdlet/StreamHelper.cs", - "commands/utility/WebCmdlet/WebCmdletElementCollection.cs", - "commands/utility/WebCmdlet/WebRequestMethod.cs", - "commands/utility/WebCmdlet/WebRequestPSCmdlet.cs", - "commands/utility/WebCmdlet/WebRequestSession.cs", - "commands/utility/WebCmdlet/WebResponseHelper.cs", - "commands/utility/WebCmdlet/WebResponseObject.cs", - "commands/utility/WebCmdlet/WebResponseObjectFactory.cs", + "commands/utility/ShowCommand/ShowCommandProxy.cs", "commands/utility/convert-HTML.cs", "commands/utility/update-list.cs", "singleshell/installer/MshUtilityMshSnapin.cs", - - "gen/FormatAndOut_out_gridview.cs", + "commands/utility/WebCmdlet/FullClr/BasicHtmlWebResponseObject.FullClr.cs", + "commands/utility/WebCmdlet/FullClr/ContentHelper.FullClr.cs", + "commands/utility/WebCmdlet/FullClr/HtmlWebResponseObject.FullClr.cs", + "commands/utility/WebCmdlet/FullClr/InvokeRestMethodCommand.FullClr.cs", + "commands/utility/WebCmdlet/FullClr/InvokeWebRequestCommand.FullClr.cs", + "commands/utility/WebCmdlet/FullClr/JsonObjectTypeResolver.cs", + "commands/utility/WebCmdlet/FullClr/WebRequestPSCmdlet.FullClr.cs", + "commands/utility/WebCmdlet/FullClr/WebResponseHelper.FullClr.cs", + "commands/utility/WebCmdlet/FullClr/WebResponseObject.FullClr.cs", + "commands/utility/WebCmdlet/FullClr/WebResponseObjectFactory.FullClr.cs", + "commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs", + + "gen/FormatAndOut_out_gridview.cs", "gen/ImplicitRemotingStrings.cs", "gen/UtilityMshSnapinResources.cs", "gen/OutPrinterDisplayStrings.cs", @@ -96,15 +90,36 @@ "System.Diagnostics.TextWriterTraceListener": "4.0.0" } }, - "net451": { + "net451": { "frameworkAssemblies": { "System.Drawing": "", "System.Web": "", "System.Web.Extensions": "", - "Microsoft.JScript": "" + "Microsoft.JScript": "", }, + "dependencies": { "Microsoft.mshtml": "1.0.0" + }, + + "buildOptions": { + "define": [ "FULLCLR" ], + "compile": { + "exclude": [ + "commands/utility/WebCmdlet/CoreCLR/BasicHtmlWebResponseObject.CoreClr.cs", + "commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs", + "commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs", + "commands/utility/WebCmdlet/CoreCLR/HttpKnownHeaderNames.cs", + "commands/utility/WebCmdlet/CoreCLR/InvokeRestMethodCommand.CoreClr.cs", + "commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs", + "commands/utility/WebCmdlet/CoreCLR/Resources.cs", + "commands/utility/WebCmdlet/CoreCLR/WebProxy.cs", + "commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs", + "commands/utility/WebCmdlet/CoreCLR/WebResponseHelper.CoreClr.cs", + "commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs", + "commands/utility/WebCmdlet/CoreCLR/WebResponseObjectFactory.CoreClr.cs" + ] + } } } } diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs index ceb375ac45..b5d4f585f3 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs @@ -1577,7 +1577,8 @@ namespace Microsoft.PowerShell public override Coordinates CursorPosition { get { return new Coordinates(Console.CursorLeft, Console.CursorTop); } - set { Console.SetCursorPosition(value.X, value.Y); } + set { Console.SetCursorPosition(value.X < 0 ? 0 : value.X, + value.Y < 0 ? 0 : value.Y); } } /// @@ -1796,54 +1797,49 @@ namespace Microsoft.PowerShell public override void SetBufferContents(Coordinates origin, BufferCell[,] contents) { - //if there are no contents, there is nothing to set the buffer to + //if there are no contents, there is nothing to set the buffer to if (contents == null) { PSTraceSource.NewArgumentNullException("contents"); } - //variables to traverse through the buffer - int cursorX = origin.X; - int cursorY = origin.Y; - //if the cursor is on the last line, we need to make more space to print the specified buffer - if (cursorY == Console.BufferHeight -1 && cursorX >= Console.BufferWidth) - { - //for each row in the buffer, create a new line + if (origin.Y == BufferSize.Height - 1 && origin.X >= BufferSize.Width) + { + //for each row in the buffer, create a new line int rows = contents.GetLength(0); - for (int i=0; i < rows; i++) + ScrollBuffer(rows); + // for each row in the buffer, move the cursor y up to the beginning of the created blank space + // but not above zero + if (origin.Y >= rows) { - ScrollBuffer(1); + origin.Y -= rows; } - - //for each row in the buffer, move the cursor y up to the beginning of the created blank space - cursorY -= rows; } - //iterate through the buffer to set + //iterate through the buffer to set foreach (var charitem in contents) { - //set the cursor to false to prevent cursor flicker + //set the cursor to false to prevent cursor flicker Console.CursorVisible = false; - - //if x is exceeding buffer width, reset to the next line - if (cursorX >= Console.BufferWidth) - { - cursorX = 1; - } - //write the character from contents + //if x is exceeding buffer width, reset to the next line + if (origin.X >= BufferSize.Width) + { + origin.X = 1; + } + + //write the character from contents Console.Out.Write(charitem.Character); //advance the character one position - cursorX++; - } + origin.X++; + } //reset the cursor to the original position - Console.SetCursorPosition(cursorX, cursorY); - //reset the cursor to visible - Console.CursorVisible = true; - + CursorPosition = origin; + //reset the cursor to visible + Console.CursorVisible = true; } /// diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressPane.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressPane.cs index 9d457a1a6d..466864c0de 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressPane.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressPane.cs @@ -102,32 +102,29 @@ namespace Microsoft.PowerShell location.X = 0; location.Y = Math.Min(location.Y + 2, bufSize.Height); - if (System.Management.Automation.Platform.IsWindows) - { - // Save off the current contents of the screen buffer in the region that we will occupy - savedRegion = - rawui.GetBufferContents( - new Rectangle(location.X, location.Y, location.X + cols - 1, location.Y + rows - 1)); - } +#if UNIX + // replace the saved region in the screen buffer with our progress display + location = rawui.CursorPosition; - //Platform is either OSX or Linux - else + //set the cursor position back to the beginning of the region to overwrite write-progress + //if the cursor is at the bottom, back it up to overwrite the previous write progress + if (location.Y >= rawui.BufferSize.Height - rows) { - // replace the saved region in the screen buffer with our progress display - location.X = rawui.CursorPosition.X; - location.Y = rawui.CursorPosition.Y; - - //set the cursor position back to the beginning of the region to overwrite write-progress - //if the cursor is at the bottom, back it up to overwrite the previous write progress - if (location.Y >= Console.BufferHeight - rows) + Console.Out.Write('\n'); + if (location.Y >= rows) { - Console.Out.Write('\n'); location.Y -= rows; } - - Console.SetCursorPosition(location.X, location.Y); } - + + rawui.CursorPosition = location; +#else + // Save off the current contents of the screen buffer in the region that we will occupy + savedRegion = + rawui.GetBufferContents( + new Rectangle(location.X, location.Y, location.X + cols - 1, location.Y + rows - 1)); +#endif + // replace the saved region in the screen buffer with our progress display rawui.SetBufferContents(location, tempProgressRegion); } diff --git a/src/Modules/Windows+Unix-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows+Unix-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index 3564ea3e1b..180cda8179 100644 --- a/src/Modules/Windows+Unix-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Windows+Unix-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -7,6 +7,7 @@ ModuleVersion="3.1.0.0" PowerShellVersion="3.0" CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide", "Out-File", "Out-String", "Get-FormatData", "Export-FormatData", "ConvertFrom-Json", "ConvertTo-Json", + "Invoke-RestMethod", "Invoke-WebRequest", "Register-ObjectEvent", "Register-EngineEvent", "Wait-Event", "Get-Event", "Remove-Event", "Get-EventSubscriber", "Unregister-Event", "New-Event", "Add-Member", "Add-Type", "Compare-Object", "ConvertFrom-StringData", "Export-Csv", "Import-Csv", "ConvertTo-Csv", "ConvertFrom-Csv", "Export-Alias", diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 new file mode 100644 index 0000000000..a92c5c112b --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 @@ -0,0 +1,596 @@ +# +# Copyright (c) Microsoft Corporation, 2016 +# +# This is a Pester test suite which validate the Web cmdlets. +# +# Note: These tests use data from http://httpbin.org/ +# + +# Invokes the given command via script block invocation. +# +function ExecuteWebCommand +{ + param ( + [ValidateNotNullOrEmpty()] + [string] + $command + ) + + $result = [PSObject]@{Output = $null; Error = $null} + + try + { + $scriptBlock = [scriptblock]::Create($command) + $result.Output = & $scriptBlock + } + catch + { + $result.Error = $_ + } + + return $result +} + +# This function calls either Invoke-WebRequest or Invoke-RestMethod using the OutFile parameter +# Then, the file content is read and return in a $result object. +# +function ExecuteRequestWithOutFile +{ + param ( + [ValidateSet("Invoke-RestMethod", "Invoke-WebRequest" )] + [string] + $cmdletName, + [string] + $uri = "http://httpbin.org/get" + ) + + $result = [PSObject]@{Output = $null; Error = $null} + $filePath = Join-Path $TestDrive ((Get-Random).ToString() + ".txt") + try + { + if ($cmdletName -eq "Invoke-WebRequest") + { + Invoke-WebRequest -Uri $uri -OutFile $filePath + } + else + { + Invoke-RestMethod -Uri $uri -OutFile $filePath + } + $result.Output = Get-Content $filePath -Raw -ea SilentlyContinue + } + catch + { + $result.Error = $_ + } + finally + { + if (Test-Path $filePath) + { + Remove-Item $filePath -Force -ea SilentlyContinue + } + } + return $result +} + +# This function calls either Invoke-WebRequest or Invoke-RestMethod with the given uri +# using the Headers parameter to disable keep-alive. +# +function ExecuteRequestWithHeaders +{ + param ( + [ValidateSet("Invoke-RestMethod", "Invoke-WebRequest" )] + [string] + $cmdletName, + [string] + $uri = "http://httpbin.org/get" + ) + + $result = [PSObject]@{Output = $null; Error = $null} + try + { + $headers = @{ Connection = 'close'} + if ($cmdletName -eq "Invoke-WebRequest") + { + $result.Output = Invoke-WebRequest -Uri $uri -TimeoutSec 5 -Headers $headers + } + else + { + $result.Output = Invoke-RestMethod -Uri $uri -TimeoutSec 5 -Headers $headers + } + } + catch + { + $result.Error = $_ + } + return $result +} + +# Returns test data for the given content type. +# +function GetTestData +{ + param( + [ValidateSet("text/plain", "application/xml", "application/json")] + [String] + $contentType + ) + + $testData = @{ItemID = 987123; Name = 'TestData'} + + if ($contentType -eq "text/plain") + { + $body = $testData | Out-String + } + + elseif ($contentType -eq "application/xml") + { + $body = ' + + + +987123 +TestData + + +' + } + + else # "application/json" + { + $body = $testData | ConvertTo-Json -Compress + } + + return $body +} + +Describe "Invoke-WebRequest tests" { + + # Validate the output of Invoke-WebRequest + # + function ValidateResponse + { + param ($response) + + $response.Error | Should Be $null + + # A sucessful call returns: Status = 200, and StatusDescription = "OK" + $response.Output.StatusDescription | Should Match "OK" + $response.Output.StatusCode | Should Be 200 + + # Make sure the response contains the following properties: + $response.Output.RawContent | Should Not Be $null + $response.Output.Headers | Should Not Be $null + $response.Output.RawContent | Should Not Be $null + $response.Output.RawContentLength | Should Not Be $null + $response.Output.Content | Should Not Be $null + } + + It "Invoke-WebRequest returns User-Agent" { + + $command = "Invoke-WebRequest -Uri http://httpbin.org/user-agent -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + ValidateResponse -response $result + + # Validate response content + $jsonContent = $result.Output.Content | ConvertFrom-Json + $jsonContent.'User-Agent' | Should Match "WindowsPowerShell" + } + + It "Invoke-WebRequest returns headers dictionary" { + + $command = "Invoke-WebRequest -Uri http://httpbin.org/headers -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + ValidateResponse -response $result + + # Validate response content + $jsonContent = $result.Output.Content | ConvertFrom-Json + $jsonContent.headers.'Accept-Encoding' | Should Match "gzip, ?deflate" + $jsonContent.headers.Host | Should Match "httpbin.org" + $jsonContent.headers.'User-Agent' | Should Match "WindowsPowerShell" + } + + It "Validate Invoke-WebRequest -DisableKeepAlive" { + + # Operation options + $uri = "http://httpbin.org/get" + $command = "Invoke-WebRequest -Uri $uri -TimeoutSec 5 -DisableKeepAlive" + + $result = ExecuteWebCommand -command $command + ValidateResponse -response $result + + $result.Output.Headers["Connection"] | Should Be "Close" + } + + It "Validate Invoke-WebRequest -MaximumRedirection" { + + $command = "Invoke-WebRequest -Uri 'http://httpbin.org/redirect/3' -MaximumRedirection 4 -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + ValidateResponse -response $result + + # Validate response content + $jsonContent = $result.Output.Content | ConvertFrom-Json + $jsonContent.headers.Host | Should Match "httpbin.org" + $jsonContent.headers.'User-Agent' | Should Match "WindowsPowerShell" + } + + It "Validate Invoke-WebRequest error for -MaximumRedirection" { + + $command = "Invoke-WebRequest -Uri 'http://httpbin.org/redirect/3' -MaximumRedirection 2 -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + $result.Error.FullyQualifiedErrorId | Should Be "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand" + } + + It "Invoke-WebRequest supports request that returns page containing UTF-8 data." { + + $command = "Invoke-WebRequest -Uri http://httpbin.org/encoding/utf8 -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + ValidateResponse -response $result + + # Validate response content + # TODO: There is a bug on ConvertFrom-Json that fails for utf8. + <# + $jsonContent = $result.Output.Content | ConvertFrom-Json + $jsonContent.headers.'Accept-Encoding' | Should Match "gzip, deflate" + $jsonContent.headers.Host | Should Match "httpbin.org" + $jsonContent.headers.'User-Agent' | Should Match "WindowsPowerShell" + #> + } + + It "Invoke-WebRequest validate timeout option" { + + $command = "Invoke-WebRequest -Uri http://httpbin.org/delay/:5 -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + $result.Error.FullyQualifiedErrorId | Should Be "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand" + + } + + # Perform the following operation for Invoke-WebRequest + # gzip Returns gzip-encoded data. + # deflate Returns deflate-encoded data. + # $dataEncodings = @("Chunked", "Compress", "Deflate", "GZip", "Identity") + # Note: These are the supported options, but we do not have a web service to test them all. + # $dataEncodings = @("gzip", "deflate") --> Currently there is a bug for deflate encoding. Please see '7976639:Invoke-WebRequest does not support -TransferEncoding deflate' for more info. + $dataEncodings = @("gzip") + foreach ($data in $dataEncodings) + { + It "Invoke-WebRequest supports request that returns $data-encoded data." { + + $command = "Invoke-WebRequest -Uri http://httpbin.org/$data -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + ValidateResponse -response $result + + # Validate response content + $jsonContent = $result.Output.Content | ConvertFrom-Json + if ($data -eq "gzip") + { + $jsonContent.gzipped | Should Match $true + } + else + { + $jsonContent.deflated | Should Match $true + } + } + } + + # Perform the following operation for Invoke-WebRequest using the following content types: "text/plain", "application/xml", "application/xml" + # post Returns POST data. + # patch Returns PATCH data. + # put Returns PUT data. + # delete Returns DELETE data + $testMethods = @("GET", "POST", "PATCH", "PUT", "DELETE") + $contentTypes = @("text/plain", "application/xml", "application/json") + + foreach ($contentType in $contentTypes) + { + foreach ($method in $testMethods) + { + # Operation options + $operation = $method.ToLower() + $uri = "http://httpbin.org/$operation" + $body = GetTestData -contentType $contentType + + if ($method -eq "GET") + { + $command = "Invoke-WebRequest -Uri $uri" + } + else + { + $command = "Invoke-WebRequest -Uri $uri -Body '$body' -Method $method -ContentType $contentType -TimeoutSec 5" + } + + It "$command" { + + $result = ExecuteWebCommand -command $command + ValidateResponse -response $result + + # Validate response content + $jsonContent = $result.Output.Content | ConvertFrom-Json + $jsonContent.url | Should Match $uri + $jsonContent.headers.'Accept-Encoding' | Should Match "gzip, ?deflate" + $jsonContent.headers.'User-Agent' | Should Match "WindowsPowerShell" + + # For a GET request, there is no data property to validate. + if ($method -ne "GET") + { + $jsonContent.headers.'Content-Type' | Should Match $contentType + + # Validate that the response Content.data field is the same as what we sent. + if ($contentType -eq "application/xml") + { + $jsonContent.data | Should Be $body + } + else + { + $jsonContent.data | Should Match $body + } + } + } + } + } + + It "Validate Invoke-WebRequest -Headers --> Set KeepAlive to false via headers" { + + $uri = "http://httpbin.org/get" + $result = ExecuteRequestWithHeaders -cmdletName Invoke-WebRequest -uri $uri + ValidateResponse -response $result + $result.Output.Headers["Connection"] | Should Be "Close" + } + + # Validate all available user agents for Invoke-WebRequest + $agents = @{InternetExplorer = "MSIE 9.0" + Chrome = "Chrome" + Opera = "Opera" + Safari = "Safari" + FireFox = "Firefox" + } + + foreach ($agentName in $agents.Keys) + { + $expectedAgent = $agents[$agentName] + $uri = "http://httpbin.org/get" + $userAgent = "[Microsoft.PowerShell.Commands.PSUserAgent]::$agentName" + $command = "Invoke-WebRequest -Uri $uri -UserAgent ($userAgent) -TimeoutSec 5" + + It "Validate Invoke-WebRequest UserAgent. Execute--> $command" { + + $result = ExecuteWebCommand -command $command + ValidateResponse -response $result + + # Validate response content + $jsonContent = $result.Output.Content | ConvertFrom-Json + $jsonContent.headers.Host | Should Match "httpbin.org" + $jsonContent.headers.'User-Agent' | Should Match $expectedAgent + } + } + + It "Validate Invoke-WebRequest -OutFile" { + + $uri = "http://httpbin.org/get" + $result = ExecuteRequestWithOutFile -cmdletName "Invoke-WebRequest" -uri $uri + $jsonContent = $result.Output | ConvertFrom-Json + $jsonContent.headers.Host | Should Match "httpbin.org" + $jsonContent.headers.'User-Agent' | Should Match "WindowsPowerShell" + } + +} + +Describe "Invoke-RestMethod tests" { + + It "Invoke-RestMethod returns User-Agent" { + + $command = "Invoke-RestMethod -Uri http://httpbin.org/user-agent -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + + # Validate response + $result.Output.'User-Agent' | Should Match "WindowsPowerShell" + } + + It "Invoke-RestMethod returns headers dictionary" { + + $command = "Invoke-RestMethod -Uri http://httpbin.org/headers -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + + # Validate response + $result.Output.headers.'Accept-Encoding' | Should Match "gzip, ?deflate" + $result.Output.headers.Host | Should Match "httpbin.org" + $result.Output.headers.'User-Agent' | Should Match "WindowsPowerShell" + } + + It "Validate Invoke-RestMethod -DisableKeepAlive" { + + # Operation options + $command = "Invoke-RestMethod -Uri 'http://httpbin.org/get' -TimeoutSec 5 -DisableKeepAlive" + + $result = ExecuteWebCommand -command $command + + # Validate response + $result.Output.headers.'Accept-Encoding' | Should Match "gzip, ?deflate" + $result.Output.headers.Host | Should Match "httpbin.org" + $result.Output.headers.'User-Agent' | Should Match "WindowsPowerShell" + + # Unfortunately, the connection information is not display in the output of Invoke-RestMethod + #$result.Output.Headers["Connection"] | Should Be "Close" + } + + It "Validate Invoke-RestMethod -MaximumRedirection" { + + $command = "Invoke-RestMethod -Uri 'http://httpbin.org/redirect/3' -MaximumRedirection 4 -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + + # Validate response + $result.Output.headers.Host | Should Match "httpbin.org" + $result.Output.headers.'User-Agent' | Should Match "WindowsPowerShell" + } + + It "Validate Invoke-RestMethod error for -MaximumRedirection" { + + $command = "Invoke-RestMethod -Uri 'http://httpbin.org/redirect/3' -MaximumRedirection 2 -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + $result.Error.FullyQualifiedErrorId | Should Be "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand" + } + + <# + It "Invoke-RestMethod supports request that returns page containing UTF-8 data." { + + $command = "Invoke-RestMethod -Uri http://httpbin.org/encoding/utf8 -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + + # Validate response content + # TODO: There is a bug on ConvertFrom-Json that fails for utf8. + $result.headers.'Accept-Encoding' | Should Match "gzip, deflate" + $result.headers.Host | Should Match "httpbin.org" + $result.headers.'User-Agent' | Should Match "WindowsPowerShell" + + } + #> + + It "Invoke-RestMethod validate timeout option" { + + $command = "Invoke-RestMethod -Uri http://httpbin.org/delay/:5 -TimeoutSec 2" + + $result = ExecuteWebCommand -command $command + $result.Error.FullyQualifiedErrorId | Should Be "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand" + + } + + # Perform the following operation for Invoke-RestMethod + # gzip Returns gzip-encoded data. + # deflate Returns deflate-encoded data. + # $dataEncodings = @("Chunked", "Compress", "Deflate", "GZip", "Identity") + # Note: These are the supported options, but we do not have a web service to test them all. + # $dataEncodings = @("gzip", "deflate") --> Currently there is a bug for deflate encoding. Please see '7976639:Invoke-RestMethod does not support -TransferEncoding deflate' for more info. + $dataEncodings = @("gzip") + foreach ($data in $dataEncodings) + { + It "Invoke-RestMethod supports request that returns $data-encoded data." { + + $command = "Invoke-RestMethod -Uri http://httpbin.org/$data -TimeoutSec 5" + + $result = ExecuteWebCommand -command $command + + # Validate response + if ($data -eq "gzip") + { + $result.Output.gzipped | Should Match $true + } + else + { + $result.Output.deflated | Should Match $true + } + } + } + + # Perform the following operation for Invoke-RestMethod using the following content types: "text/plain", "application/xml", "application/xml" + # post Returns POST data. + # patch Returns PATCH data. + # put Returns PUT data. + # delete Returns DELETE data + $testMethods = @("GET", "POST", "PATCH", "PUT", "DELETE") + $contentTypes = @("text/plain", "application/xml", "application/json") + + foreach ($contentType in $contentTypes) + { + foreach ($method in $testMethods) + { + # Operation options + $operation = $method.ToLower() + $uri = "http://httpbin.org/$operation" + $body = GetTestData -contentType $contentType + + if ($method -eq "GET") + { + $command = "Invoke-RestMethod -Uri $uri" + } + else + { + $command = "Invoke-RestMethod -Uri $uri -Body '$body' -Method $method -ContentType $contentType -TimeoutSec 5" + } + + It "$command" { + + $result = ExecuteWebCommand -command $command + + # Validate response + $result.Output.url | Should Match $uri + $result.Output.headers.'Accept-Encoding' | Should Match "gzip, ?deflate" + $result.Output.headers.'User-Agent' | Should Match "WindowsPowerShell" + + # For a GET request, there is no data property to validate. + if ($method -ne "GET") + { + $result.Output.headers.'Content-Type' | Should Match $contentType + + # Validate that the response Content.data field is the same as what we sent. + if ($contentType -eq "application/xml") + { + $result.Output.data | Should Be $body + } + else + { + $result.Output.data | Should Match $body + } + } + } + } + } + + It "Validate Invoke-RestMethod -Headers --> Set KeepAlive to false via headers" { + + $uri = "http://httpbin.org/get" + $result = ExecuteRequestWithHeaders -cmdletName Invoke-RestMethod -uri $uri + + # Validate response + $result.Output.url | Should Match $uri + $result.Output.headers.'Accept-Encoding' | Should Match "gzip, ?deflate" + $result.Output.headers.'User-Agent' | Should Match "WindowsPowerShell" + + # Unfortunately, the connection information is not display in the output of Invoke-RestMethod + #$result.Output.Headers["Connection"] | Should Be "Close" + } + + # Validate all available user agents for Invoke-RestMethod + $agents = @{InternetExplorer = "MSIE 9.0" + Chrome = "Chrome" + Opera = "Opera" + Safari = "Safari" + FireFox = "Firefox" + } + + foreach ($agentName in $agents.Keys) + { + $expectedAgent = $agents[$agentName] + $uri = "http://httpbin.org/get" + $userAgent = "[Microsoft.PowerShell.Commands.PSUserAgent]::$agentName" + $command = "Invoke-RestMethod -Uri $uri -UserAgent ($userAgent) -TimeoutSec 5" + + It "Validate Invoke-RestMethod UserAgent. Execute--> $command" { + + $result = ExecuteWebCommand -command $command + + # Validate response + $result.Output.headers.Host | Should Match "httpbin.org" + $result.Output.headers.'User-Agent' | Should Match $expectedAgent + } + } + + It "Validate Invoke-RestMethod -OutFile" { + + $uri = "http://httpbin.org/get" + $result = ExecuteRequestWithOutFile -cmdletName "Invoke-RestMethod" -uri $uri + $jsonContent = $result.Output | ConvertFrom-Json + $jsonContent.headers.Host | Should Match "httpbin.org" + $jsonContent.headers.'User-Agent' | Should Match "WindowsPowerShell" + } +}