Make Invoke-WebRequest cmdlet work on CoreCLR without mshtml

This commit is contained in:
Francisco Gamino authored and Andrew Schwartzmeyer committed 2016-07-24 16:20:46 -07:00
1 parent b7115879c3
commit 46f8e0d60d
36 files changed
+2764 -1312

No files matched your search

@@ -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
/// <summary>
/// Response object for html content without DOM parsing
/// </summary>
public class BasicHtmlWebResponseObject : WebResponseObject
public partial class BasicHtmlWebResponseObject : WebResponseObject
{
#region Properties
@@ -109,30 +108,6 @@ namespace Microsoft.PowerShell.Commands
#endregion Properties
#region Constructors
/// <summary>
/// Constructor for HtmlWebResponseObject
/// </summary>
/// <param name="response"></param>
public BasicHtmlWebResponseObject(WebResponse response)
: this(response, null) { }
/// <summary>
/// Constructor for HtmlWebResponseObject with memory stream
/// </summary>
/// <param name="response"></param>
/// <param name="contentStream"></param>
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
@@ -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
}
}
@@ -1,26 +1,25 @@
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
/********************************************************************++
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;
#if !CORECLR
using mshtml;
#endif
namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// Response object for html content
/// </summary>
public class HtmlWebResponseObject : WebResponseObject, IDisposable
public partial class HtmlWebResponseObject : WebResponseObject, IDisposable
{
#region Properties
@@ -29,6 +28,7 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
public new string Content { get; private set; }
#if !CORECLR
// The HTML document
private IHTMLDocument2 _parsedHtml;
@@ -43,10 +43,10 @@ namespace Microsoft.PowerShell.Commands
// The exception thrown during the parsing
private Exception _parsingException;
#endif
// The current execution context
private readonly ExecutionContext _executionContext;
#if !CORECLR
// The flag that notifies the worker thread to stop loading the document
private bool _stopWorkerThread;
@@ -221,59 +221,19 @@ namespace Microsoft.PowerShell.Commands
return _allElements;
}
}
#endif
#endregion Properties
#region Constructors
/// <summary>
/// Constructor for HtmlWebResponseObject
/// </summary>
/// <param name="response"></param>
/// <param name="executionContext"></param>
internal HtmlWebResponseObject(WebResponse response, ExecutionContext executionContext)
: this(response, null, executionContext) { }
/// <summary>
/// Constructor for HtmlWebResponseObject with memory stream
/// </summary>
/// <param name="response"></param>
/// <param name="contentStream"></param> /// <param name="executionContext"></param>
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
#if !CORECLR
private static Regex _tagRegex;
private static Regex _attribsRegex;
private static Regex _attribNameValueRegex;
#endif
#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();
}
#if !CORECLR
// The "onreadystatechange" event handler
private void ReadyStateChanged(IHTMLEventObj obj)
{
@@ -494,7 +454,7 @@ namespace Microsoft.PowerShell.Commands
{
return (null == element ? null : element.id);
}
#endif
/// <summary>
/// Reads the response content from the web response.
/// </summary>
@@ -512,8 +472,9 @@ namespace Microsoft.PowerShell.Commands
this.Content = string.Empty;
}
}
#endregion Methods
#endregion Methods
#if !CORECLR
/// <summary>
/// Dispose the the instance of the class.
/// </summary>
@@ -548,5 +509,6 @@ namespace Microsoft.PowerShell.Commands
System.Runtime.InteropServices.Marshal.ReleaseComObject(_parsedHtml);
}
}
#endif
}
}
@@ -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
/// <summary>
/// gets or sets the parameter Method
/// </summary>
[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
/// <summary>
/// enum for rest return type.
/// </summary>
public enum RestReturnType
{
/// <summary>
/// Return type not defined in reponse,
/// best effort detect
/// </summary>
Detect,
/// <summary>
/// Json return type
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
Json,
/// <summary>
/// Xml return type
/// </summary>
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();
}
}
}
}
@@ -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
{
/// <summary>
/// Base class for Invoke-RestMethod and Invoke-WebRequest commands.
/// </summary>
public abstract partial class WebRequestPSCmdlet : PSCmdlet
{
#region Virtual Properties
#region URI
/// <summary>
/// gets or sets the parameter UseBasicParsing
/// </summary>
[Parameter]
public virtual SwitchParameter UseBasicParsing { get; set; }
/// <summary>
/// gets or sets the Uri property
/// </summary>
[Parameter(Position = 0, Mandatory = true)]
[ValidateNotNullOrEmpty]
public virtual Uri Uri { get; set; }
#endregion
#region Session
/// <summary>
/// gets or sets the Session property
/// </summary>
[Parameter]
public virtual WebRequestSession WebSession { get; set; }
/// <summary>
/// gets or sets the SessionVariable property
/// </summary>
[Parameter]
[Alias("SV")]
public virtual string SessionVariable { get; set; }
#endregion
#region Authorization and Credentials
/// <summary>
/// gets or sets the Credential property
/// </summary>
[Parameter]
[Credential]
public virtual PSCredential Credential { get; set; }
/// <summary>
/// gets or sets the UseDefaultCredentials property
/// </summary>
[Parameter]
public virtual SwitchParameter UseDefaultCredentials { get; set; }
/// <summary>
/// gets or sets the CertificateThumbprint property
/// </summary>
[Parameter]
[ValidateNotNullOrEmpty]
public virtual string CertificateThumbprint { get; set; }
/// <summary>
/// gets or sets the Certificate property
/// </summary>
[Parameter]
[ValidateNotNull]
public virtual X509Certificate Certificate { get; set; }
#endregion
#region Headers
/// <summary>
/// gets or sets the UserAgent property
/// </summary>
[Parameter]
public virtual string UserAgent { get; set; }
/// <summary>
/// gets or sets the DisableKeepAlive property
/// </summary>
[Parameter]
public virtual SwitchParameter DisableKeepAlive { get; set; }
/// <summary>
/// gets or sets the TimeOut property
/// </summary>
[Parameter]
[ValidateRange(0, Int32.MaxValue)]
public virtual int TimeoutSec { get; set; }
/// <summary>
/// gets or sets the Headers property
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
[Parameter]
public virtual IDictionary Headers { get; set; }
#endregion
#region Redirect
/// <summary>
/// gets or sets the RedirectMax property
/// </summary>
[Parameter]
[ValidateRange(0, Int32.MaxValue)]
public virtual int MaximumRedirection
{
get { return maximumRedirection; }
set { maximumRedirection = value; }
}
private int maximumRedirection = -1;
#endregion
#region Method
/// <summary>
/// gets or sets the Method property
/// </summary>
[Parameter]
public virtual WebRequestMethod Method
{
get { return method; }
set { method = value; }
}
private WebRequestMethod method = WebRequestMethod.Default;
#endregion
#region Proxy
/// <summary>
/// gets or sets the Proxy property
/// </summary>
[Parameter]
public virtual Uri Proxy { get; set; }
/// <summary>
/// gets or sets the ProxyCredential property
/// </summary>
[Parameter]
[Credential]
public virtual PSCredential ProxyCredential { get; set; }
/// <summary>
/// gets or sets the ProxyUseDefaultCredentials property
/// </summary>
[Parameter]
public virtual SwitchParameter ProxyUseDefaultCredentials { get; set; }
#endregion
#region Input
/// <summary>
/// gets or sets the Body property
/// </summary>
[Parameter(ValueFromPipeline = true)]
public virtual object Body { get; set; }
/// <summary>
/// gets or sets the ContentType property
/// </summary>
[Parameter]
public virtual string ContentType { get; set; }
/// <summary>
/// gets or sets the TransferEncoding property
/// </summary>
[Parameter]
[ValidateSet("chunked", "compress", "deflate", "gzip", "identity", IgnoreCase = true)]
public virtual string TransferEncoding { get; set; }
/// <summary>
/// gets or sets the InFile property
/// </summary>
[Parameter]
public virtual string InFile { get; set; }
/// <summary>
/// keep the original file path after the resolved provider path is
/// assigned to InFile
/// </summary>
private string _originalFilePath;
#endregion
#region Output
/// <summary>
/// gets or sets the OutFile property
/// </summary>
[Parameter]
public virtual string OutFile { get; set; }
/// <summary>
/// gets or sets the PassThrough property
/// </summary>
[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<string> 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
/// <summary>
/// Verifies that Internet Explorer is available, and that its first-run
/// configuration is complete.
/// </summary>
/// <param name="checkComObject">True if we should try to access IE's COM object. Not
/// needed if an HtmlDocument will be created shortly.</param>
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<IDictionary>(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
}
}
@@ -0,0 +1,99 @@
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// WebResponseObject
/// </summary>
public partial class WebResponseObject
{
#region Properties
/// <summary>
/// gets or protected sets the Content property
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
public byte[] Content { get; protected set; }
/// <summary>
/// gets the StatusCode property
/// </summary>
public int StatusCode
{
get { return (WebResponseHelper.GetStatusCode(BaseResponse)); }
}
/// <summary>
/// gets the StatusDescription property
/// </summary>
public string StatusDescription
{
get { return (WebResponseHelper.GetStatusDescription(BaseResponse)); }
}
private MemoryStream _rawContentStream;
/// <summary>
/// gets the RawContentStream property
/// </summary>
public MemoryStream RawContentStream
{
get { return (_rawContentStream); }
}
/// <summary>
/// gets the RawContentLength property
/// </summary>
public long RawContentLength
{
get { return (null == RawContentStream ? -1 : RawContentStream.Length); }
}
/// <summary>
/// gets or protected sets the RawContent property
/// </summary>
public string RawContent { get; protected set; }
#endregion Properties
#region Methods
/// <summary>
/// Reads the response content from the web response.
/// </summary>
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));
}
/// <summary>
/// Returns the string representation of this web response.
/// </summary>
/// <returns>The string representation of this web response.</returns>
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
}
}
@@ -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
{
/// <summary>
/// Response object for html content without DOM parsing
/// </summary>
public partial class BasicHtmlWebResponseObject : WebResponseObject
{
#region Constructors
/// <summary>
/// Constructor for HtmlWebResponseObject
/// </summary>
/// <param name="response"></param>
public BasicHtmlWebResponseObject(HttpResponseMessage response)
: this(response, null) { }
/// <summary>
/// Constructor for HtmlWebResponseObject with memory stream
/// </summary>
/// <param name="response"></param>
/// <param name="contentStream"></param>
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
@@ -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
@@ -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
{
/// <summary>
/// Response object for html content
/// </summary>
public partial class HtmlWebResponseObject : WebResponseObject, IDisposable
{
#region Constructors
/// <summary>
/// Constructor for HtmlWebResponseObject
/// </summary>
/// <param name="response"></param>
/// <param name="executionContext"></param>
internal HtmlWebResponseObject(HttpResponseMessage response, ExecutionContext executionContext)
: this(response, null, executionContext) { }
/// <summary>
/// Constructor for HtmlWebResponseObject with memory stream
/// </summary>
/// <param name="response"></param>
/// <param name="contentStream"></param> /// <param name="executionContext"></param>
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();
}
/// <summary>
/// Dispose the the instance of the class.
/// </summary>
public void Dispose()
{
GC.SuppressFinalize(this);
}
#endregion
}
}
#endif
@@ -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<string> _contentHeaderSet = null;
internal static HashSet<string> ContentHeaders
{
get {
if (_contentHeaderSet == null)
{
_contentHeaderSet = new HashSet<string>(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
@@ -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
{
/// <summary>
/// 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.
/// </summary>
[Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=217034")]
public partial class InvokeRestMethodCommand : WebRequestPSCmdlet
{
#region Virtual Method Overrides
/// <summary>
/// Process the web reponse and output corresponding objects.
/// </summary>
/// <param name="response"></param>
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
@@ -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
{
/// <summary>
/// The Invoke-RestMethod command
/// This command makes an HTTP or HTTPS request to a web server and returns the results.
/// </summary>
[Cmdlet(VerbsLifecycle.Invoke, "WebRequest", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=217035")]
public class InvokeWebRequestCommand : WebRequestPSCmdlet
{
#region Virtual Method Overrides
/// <summary>
/// Process the web reponse and output corresponding objects.
/// </summary>
/// <param name="response"></param>
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
@@ -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
@@ -0,0 +1,539 @@
#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
{
/// <summary>
/// Base class for Invoke-RestMethod and Invoke-WebRequest commands.
/// </summary>
public abstract partial class WebRequestPSCmdlet : PSCmdlet
{
#region Abstract Methods
/// <summary>
/// Read the supplied WebResponse object and push the
/// resulting output into the pipeline.
/// </summary>
/// <param name="response">Instance of a WebResponse object to be processed</param>
internal abstract void ProcessResponse(HttpResponseMessage response);
#endregion Abstract Methods
/// <summary>
/// Cancellation token source
/// </summary>
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;
}
/*
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)
{
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
/// <summary>
/// the main execution method for cmdlets derived from WebRequestPSCmdlet.
/// </summary>
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);
}
}
/// <summary>
/// Implementing ^C, after start the BeginGetResponse
/// </summary>
protected override void StopProcessing()
{
if (cancelToken != null)
{
cancelToken.Cancel();
}
}
#endregion Overrides
#region Helper Methods
/// <summary>
/// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream.
/// </summary>
/// <param name="request">The WebRequest who's content is to be set</param>
/// <param name="content">A byte array containing the content data.</param>
/// <returns>The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property</returns>
/// <remarks>
/// 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.
/// </remarks>
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;
}
/// <summary>
/// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream.
/// </summary>
/// <param name="request">The WebRequest who's content is to be set</param>
/// <param name="content">A String object containing the content data.</param>
/// <returns>The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property</returns>
/// <remarks>
/// 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.
/// </remarks>
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;
}
/// <summary>
/// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream.
/// </summary>
/// <param name="request">The WebRequest who's content is to be set</param>
/// <param name="contentStream">A Stream object containing the content data.</param>
/// <returns>The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property</returns>
/// <remarks>
/// 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.
/// </remarks>
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
@@ -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
@@ -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
{
/// <summary>
/// WebResponseObject
/// </summary>
public partial class WebResponseObject
{
#region Properties
/// <summary>
/// gets or sets the BaseResponse property
/// </summary>
public HttpResponseMessage BaseResponse { get; set; }
/// <summary>
/// gets the Headers property
/// </summary>
public Dictionary<string, IEnumerable<string>> Headers
{
get
{
var headers = new Dictionary<string, IEnumerable<string>>(StringComparer.OrdinalIgnoreCase);
foreach (var entry in BaseResponse.Headers)
{
headers[entry.Key] = entry.Value;
}
return headers;
}
}
#endregion
#region Constructors
/// <summary>
/// Constructor for WebResponseObject
/// </summary>
/// <param name="response"></param>
public WebResponseObject(HttpResponseMessage response)
: this(response, null) { }
/// <summary>
/// Constructor for WebResponseObject with contentStream
/// </summary>
/// <param name="response"></param>
/// <param name="contentStream"></param>
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
@@ -0,0 +1,37 @@
#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))
{
if (useBasicParsing)
{
output = new BasicHtmlWebResponseObject(response, responseStream);
}
else
{
output = new HtmlWebResponseObject(response, responseStream, executionContext);
}
}
else
{
output = new WebResponseObject(response, responseStream);
}
return (output);
}
}
}
#endif
@@ -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
{
@@ -0,0 +1,50 @@
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System.Net;
using System.IO;
using System.Text;
namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// Response object for html content without DOM parsing
/// </summary>
public partial class BasicHtmlWebResponseObject : WebResponseObject
{
#region Constructors
/// <summary>
/// Constructor for HtmlWebResponseObject
/// </summary>
/// <param name="response"></param>
public BasicHtmlWebResponseObject(WebResponse response)
: this(response, null) { }
/// <summary>
/// Constructor for HtmlWebResponseObject with memory stream
/// </summary>
/// <param name="response"></param>
/// <param name="contentStream"></param>
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
}
}
@@ -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;
}
}
}
@@ -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
{
/// <summary>
/// Response object for html content
/// </summary>
public partial class HtmlWebResponseObject : WebResponseObject, IDisposable
{
#region Constructors
/// <summary>
/// Constructor for HtmlWebResponseObject
/// </summary>
/// <param name="response"></param>
/// <param name="executionContext"></param>
internal HtmlWebResponseObject(WebResponse response, ExecutionContext executionContext)
: this(response, null, executionContext) { }
/// <summary>
/// Constructor for HtmlWebResponseObject with memory stream
/// </summary>
/// <param name="response"></param>
/// <param name="contentStream"></param>
/// <param name="executionContext"></param>
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
}
}
@@ -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
{
/// <summary>
/// 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.
/// </summary>
[Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=217034")]
public partial class InvokeRestMethodCommand : WebRequestPSCmdlet
{
#region Virtual Method Overrides
/// <summary>
/// Process the web reponse and output corresponding objects.
/// </summary>
/// <param name="response"></param>
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
}
}
@@ -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
}
}
}
@@ -1,4 +1,4 @@
/********************************************************************++
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
@@ -26,4 +26,4 @@ namespace Microsoft.PowerShell.Commands
return (string.Empty);
}
}
}
}
@@ -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
{
/// <summary>
/// Base class for Invoke-RestMethod and Invoke-WebRequest commands.
/// </summary>
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
/// <summary>
/// gets or sets the parameter UseBasicParsing
/// </summary>
[Parameter]
public virtual SwitchParameter UseBasicParsing { get; set; }
/// <summary>
/// gets or sets the Uri property
/// </summary>
[Parameter(Position = 0, Mandatory = true)]
[ValidateNotNullOrEmpty]
public virtual Uri Uri { get; set; }
#endregion
#region Session
/// <summary>
/// gets or sets the Session property
/// </summary>
[Parameter]
public virtual WebRequestSession WebSession { get; set; }
/// <summary>
/// gets or sets the SessionVariable property
/// </summary>
[Parameter]
[Alias("SV")]
public virtual string SessionVariable { get; set; }
#endregion
#region Authorization and Credentials
/// <summary>
/// gets or sets the Credential property
/// </summary>
[Parameter]
[Credential]
public virtual PSCredential Credential { get; set; }
/// <summary>
/// gets or sets the UseDefaultCredentials property
/// </summary>
[Parameter]
public virtual SwitchParameter UseDefaultCredentials { get; set; }
/// <summary>
/// gets or sets the CertificateThumbprint property
/// </summary>
[Parameter]
[ValidateNotNullOrEmpty]
public virtual string CertificateThumbprint { get; set; }
/// <summary>
/// gets or sets the Certificate property
/// </summary>
[Parameter]
[ValidateNotNull]
public virtual X509Certificate Certificate { get; set; }
#endregion
#region Headers
/// <summary>
/// gets or sets the UserAgent property
/// </summary>
[Parameter]
public virtual string UserAgent { get; set; }
/// <summary>
/// gets or sets the DisableKeepAlive property
/// </summary>
[Parameter]
public virtual SwitchParameter DisableKeepAlive { get; set; }
/// <summary>
/// gets or sets the TimeOut property
/// </summary>
[Parameter]
public virtual int TimeoutSec { get; set; }
/// <summary>
/// gets or sets the Headers property
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
[Parameter]
public virtual IDictionary Headers { get; set; }
#endregion
#region Redirect
/// <summary>
/// gets or sets the RedirectMax property
/// </summary>
[Parameter]
[ValidateRange(0, Int32.MaxValue)]
public virtual int MaximumRedirection
{
get { return maximumRedirection; }
set { maximumRedirection = value; }
}
private int maximumRedirection = -1;
#endregion
#region Method
/// <summary>
/// gets or sets the Method property
/// </summary>
[Parameter]
public virtual WebRequestMethod Method
{
get { return method; }
set { method = value; }
}
private WebRequestMethod method = WebRequestMethod.Default;
#endregion
#region Proxy
/// <summary>
/// gets or sets the Proxy property
/// </summary>
[Parameter]
public virtual Uri Proxy { get; set; }
/// <summary>
/// gets or sets the ProxyCredential property
/// </summary>
[Parameter]
[Credential]
public virtual PSCredential ProxyCredential { get; set; }
/// <summary>
/// gets or sets the ProxyUseDefaultCredentials property
/// </summary>
[Parameter]
public virtual SwitchParameter ProxyUseDefaultCredentials { get; set; }
#endregion
#region Input
/// <summary>
/// gets or sets the Body property
/// </summary>
[Parameter(ValueFromPipeline = true)]
public virtual object Body { get; set; }
/// <summary>
/// gets or sets the ContentType property
/// </summary>
[Parameter]
public virtual string ContentType { get; set; }
/// <summary>
/// gets or sets the TransferEncoding property
/// </summary>
[Parameter]
[ValidateSet("chunked", "compress", "deflate", "gzip", "identity", IgnoreCase = true)]
public virtual string TransferEncoding { get; set; }
/// <summary>
/// gets or sets the InFile property
/// </summary>
[Parameter]
public virtual string InFile { get; set; }
/// <summary>
/// keep the original file path after the resolved provider path is
/// assigned to InFile
/// </summary>
private string _originalFilePath;
/// <summary>
/// the WebRequest we will GetResponse for.
/// </summary>
private WebRequest _webRequest;
#endregion
#region Output
/// <summary>
/// gets or sets the OutFile property
/// </summary>
[Parameter]
public virtual string OutFile { get; set; }
/// <summary>
/// gets or sets the PassThrough property
/// </summary>
[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<string> 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<IDictionary>(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
/// <summary>
@@ -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);
}
/// <summary>
/// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream.
/// </summary>
@@ -1200,121 +737,6 @@ namespace Microsoft.PowerShell.Commands
}
/// <summary>
/// Verifies that Internet Explorer is available, and that its first-run
/// configuration is complete.
/// </summary>
/// <param name="checkComObject">True if we should try to access IE's COM object. Not
/// needed if an HtmlDocument will be created shortly.</param>
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
}
}
}
@@ -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));
}
}
}
}
@@ -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
/// <summary>
/// WebResponseObject
/// </summary>
public class WebResponseObject
public partial class WebResponseObject
{
#region Properties
/// <summary>
/// gets or protected sets the Content property
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
public byte[] Content { get; protected set; }
/// <summary>
/// gets or sets the BaseResponse property
/// </summary>
public WebResponse BaseResponse { get; set; }
/// <summary>
/// gets the StatusCode property
/// </summary>
public int StatusCode
{
get { return (WebResponseHelper.GetStatusCode(BaseResponse)); }
}
/// <summary>
/// gets the StatusDescription property
/// </summary>
public string StatusDescription
{
get { return (WebResponseHelper.GetStatusDescription(BaseResponse)); }
}
/// <summary>
/// gets the Headers property
/// </summary>
public Dictionary<string,string> Headers
public Dictionary<string, string> Headers
{
get
{
@@ -62,29 +39,7 @@ namespace Microsoft.PowerShell.Commands
}
}
private MemoryStream _rawContentStream;
/// <summary>
/// gets the RawContentStream property
/// </summary>
public MemoryStream RawContentStream
{
get { return (_rawContentStream); }
}
/// <summary>
/// gets the RawContentLength property
/// </summary>
public long RawContentLength
{
get { return (null == RawContentStream ? -1 : RawContentStream.Length); }
}
/// <summary>
/// gets or protected sets the RawContent property
/// </summary>
public string RawContent { get; protected set; }
#endregion Properties
#endregion
#region Constructors
@@ -102,7 +57,7 @@ namespace Microsoft.PowerShell.Commands
/// <param name="contentStream"></param>
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
/// <summary>
/// Reads the response content from the web response.
/// </summary>
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;
}
/// <summary>
/// Returns the string representation of this web response.
/// </summary>
/// <returns>The string representation of this web response.</returns>
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
}
}
@@ -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);
}
}
}
}
@@ -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
{
/// <summary>
/// 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.
/// </summary>
[Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=217034")]
public class InvokeRestMethodCommand : WebRequestPSCmdlet
{
#region Parameters
/// <summary>
/// gets or sets the parameter Method
/// </summary>
[Parameter]
public override WebRequestMethod Method
{
get { return base.Method; }
set { base.Method = value; }
}
#endregion Parameters
#region Virtual Method Overrides
/// <summary>
/// Process the web reponse and output corresponding objects.
/// </summary>
/// <param name="response"></param>
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
}
/// <summary>
/// enum for rest return type.
/// </summary>
public enum RestReturnType
{
/// <summary>
/// Return type not defined in reponse,
/// best effort detect
/// </summary>
Detect,
/// <summary>
/// Json return type
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
Json,
/// <summary>
/// Xml return type
/// </summary>
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();
}
}
}
@@ -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
}
}
@@ -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;
}
}
/// <summary>
///
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="count"></param>
/// <param name="callback"></param>
/// <param name="state"></param>
/// <returns></returns>
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
Initialize();
return base.BeginRead(buffer, offset, count, callback, state);
}
/// <summary>
///
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="count"></param>
/// <param name="callback"></param>
/// <param name="state"></param>
/// <returns></returns>
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
Initialize();
return base.BeginWrite(buffer, offset, count, callback, state);
}
/// <summary>
///
/// </summary>
public override void Close()
{
base.Close();
}
/// <summary>
///
@@ -151,16 +117,6 @@ namespace Microsoft.PowerShell.Commands
return base.CopyToAsync(destination, bufferSize, cancellationToken);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override byte[] GetBuffer()
{
Initialize();
return base.GetBuffer();
}
/// <summary>
///
/// </summary>
@@ -264,6 +220,51 @@ namespace Microsoft.PowerShell.Commands
base.WriteTo(stream);
}
/// <summary>
///
/// </summary>
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
#if !CORECLR
/// <summary>
///
/// </summary>
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
Initialize();
return base.BeginRead(buffer, offset, count, callback, state);
}
/// <summary>
///
/// </summary>
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
Initialize();
return base.BeginWrite(buffer, offset, count, callback, state);
}
/// <summary>
///
/// </summary>
public override byte[] GetBuffer()
{
Initialize();
return base.GetBuffer();
}
/// <summary>
///
/// </summary>
public override void Close()
{
base.Close();
}
#endif
/// <summary>
///
/// </summary>
@@ -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
}
@@ -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
{
/// <summary>
@@ -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<string,string> Headers { get; set; }
#if CORECLR
/// <summary>
/// gets or sets the content Headers when using HttpClient
/// </summary>
internal Dictionary<string, string> ContentHeaders { get; set; }
#endif
/// <summary>
/// gets or sets the Cookies property
/// </summary>
@@ -60,7 +66,7 @@ namespace Microsoft.PowerShell.Commands
/// gets or sets the RedirectMax property
/// </summary>
public int MaximumRedirection { get; set; }
/// <summary>
/// Construct a new instance of a WebRequestSession object.
/// </summary>
@@ -68,6 +74,9 @@ namespace Microsoft.PowerShell.Commands
{
// build the headers collection
Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
#if CORECLR
ContentHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
#endif
// build the cookie jar
Cookies = new CookieContainer();
@@ -21,7 +21,6 @@
"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/Resources.cs": "CoreCLR/Resources.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",
@@ -40,29 +40,22 @@
"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",
"gen/FormatAndOut_out_gridview.cs",
"gen/ImplicitRemotingStrings.cs",
"gen/UtilityMshSnapinResources.cs",
"gen/OutPrinterDisplayStrings.cs",
@@ -96,15 +89,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"
]
}
}
}
}
@@ -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",