/
githubmirror
/
PowerShell
Обзор
Документация
Войти
/
githubmirror
/
PowerShell
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs
251 строка
10 KB
xtqqczze
Update `LangVersion` to `preview` (#26214)
19 фев 2026, 18:39
Не верифицирован
19 фев 2026, 18:39
2af5ad1
Код
Авторство
О чём код?
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Management.Automation; using System.Net.Http; using System.Text; using System.Text.RegularExpressions; using System.Threading; namespace Microsoft.PowerShell.Commands { /// <summary> /// Response object for html content without DOM parsing. /// </summary> public class BasicHtmlWebResponseObject : WebResponseObject { #region Constructors /// <summary> /// Initializes a new instance of the <see cref="BasicHtmlWebResponseObject"/> class. /// </summary> /// <param name="response">The response.</param> /// <param name="perReadTimeout">Time permitted between reads or Timeout.InfiniteTimeSpan for no timeout.</param> /// <param name="cancellationToken">Cancellation token.</param> public BasicHtmlWebResponseObject(HttpResponseMessage response, TimeSpan perReadTimeout, CancellationToken cancellationToken) : this(response, null, perReadTimeout, cancellationToken) { } /// <summary> /// Initializes a new instance of the <see cref="BasicHtmlWebResponseObject"/> class /// with the specified <paramref name="contentStream"/>. /// </summary> /// <param name="response">The response.</param> /// <param name="contentStream">The content stream associated with the response.</param> /// <param name="perReadTimeout">Time permitted between reads or Timeout.InfiniteTimeSpan for no timeout.</param> /// <param name="cancellationToken">Cancellation token.</param> public BasicHtmlWebResponseObject(HttpResponseMessage response, Stream? contentStream, TimeSpan perReadTimeout, CancellationToken cancellationToken) : base(response, contentStream, perReadTimeout, cancellationToken) { InitializeContent(cancellationToken); InitializeRawContent(response); } #endregion Constructors #region Properties /// <summary> /// Gets the text body content of this response. /// </summary> /// <value> /// Content of the response body, decoded using <see cref="Encoding"/>, /// if the <c>Content-Type</c> response header is a recognized text /// type. Otherwise <see langword="null"/>. /// </value> public new string Content { get; private set; } /// <summary> /// Gets the encoding of the text body content of this response. /// </summary> /// <value> /// Encoding of the response body from the <c>Content-Type</c> header, /// or <see langword="null"/> if the encoding could not be determined. /// </value> public Encoding? Encoding { get; private set; } private WebCmdletElementCollection? _inputFields; /// <summary> /// Gets the HTML input field elements parsed from <see cref="Content"/>. /// </summary> public WebCmdletElementCollection InputFields { get { if (_inputFields == null) { List<PSObject> parsedFields = new(); MatchCollection fieldMatch = HtmlParser.InputFieldRegex.Matches(Content); foreach (Match match in fieldMatch) { parsedFields.Add(CreateHtmlObject(match.Value, "INPUT")); } _inputFields = new WebCmdletElementCollection(parsedFields); } return _inputFields; } } private WebCmdletElementCollection? _links; /// <summary> /// Gets the HTML a link elements parsed from <see cref="Content"/>. /// </summary> public WebCmdletElementCollection Links { get { if (_links == null) { List<PSObject> parsedLinks = new(); MatchCollection linkMatch = HtmlParser.LinkRegex.Matches(Content); foreach (Match link in linkMatch) { parsedLinks.Add(CreateHtmlObject(link.Value, "A")); } _links = new WebCmdletElementCollection(parsedLinks); } return _links; } } private WebCmdletElementCollection? _images; /// <summary> /// Gets the HTML img elements parsed from <see cref="Content"/>. /// </summary> public WebCmdletElementCollection Images { get { if (_images == null) { List<PSObject> parsedImages = new(); MatchCollection imageMatch = HtmlParser.ImageRegex.Matches(Content); foreach (Match image in imageMatch) { parsedImages.Add(CreateHtmlObject(image.Value, "IMG")); } _images = new WebCmdletElementCollection(parsedImages); } return _images; } } #endregion Properties #region Methods /// <summary> /// Reads the response content from the web response. /// </summary> /// <param name="cancellationToken">The cancellation token.</param> [MemberNotNull(nameof(Content))] protected void InitializeContent(CancellationToken cancellationToken) { string? contentType = ContentHelper.GetContentType(BaseResponse); if (ContentHelper.IsText(contentType)) { // Fill the Content buffer string? characterSet = WebResponseHelper.GetCharacterSet(BaseResponse); Content = StreamHelper.DecodeStream(RawContentStream, characterSet, out Encoding encoding, perReadTimeout, cancellationToken); Encoding = encoding; } else { Content = string.Empty; } } private static PSObject CreateHtmlObject(string html, string tagName) { PSObject elementObject = new(); elementObject.Properties.Add(new PSNoteProperty("outerHTML", html)); elementObject.Properties.Add(new PSNoteProperty("tagName", tagName)); ParseAttributes(html, elementObject); return elementObject; } private void InitializeRawContent(HttpResponseMessage baseResponse) { StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); raw.Append(Content); RawContent = raw.ToString(); } private static void ParseAttributes(string outerHtml, PSObject elementObject) { // We might get an empty input for a directive from the HTML file if (!string.IsNullOrEmpty(outerHtml)) { // Extract just the opening tag of the HTML element (omitting the closing tag and any contents, // including contained HTML elements) Match match = HtmlParser.TagRegex.Match(outerHtml); // Extract all the attribute specifications within the HTML element opening tag MatchCollection attribMatches = HtmlParser.AttribsRegex.Matches(match.Value); foreach (Match attribMatch in attribMatches) { // Extract the name and value for this attribute (allowing for variations like single/double/no // quotes, and no value at all) Match nvMatches = HtmlParser.AttribNameValueRegex.Match(attribMatch.Value); Debug.Assert(nvMatches.Groups.Count == 5); // Name is always captured by group #1 string name = nvMatches.Groups[1].Value; // The value (if any) is captured by group #2, #3, or #4, depending on quoting or lack thereof string? value = null; if (nvMatches.Groups[2].Success) { value = nvMatches.Groups[2].Value; } else if (nvMatches.Groups[3].Success) { value = nvMatches.Groups[3].Value; } else if (nvMatches.Groups[4].Success) { value = nvMatches.Groups[4].Value; } elementObject.Properties.Add(new PSNoteProperty(name, value)); } } } #endregion Methods // This class is needed so the static Regexes are initialized only the first time they are used private static class HtmlParser { internal static readonly Regex AttribsRegex = new Regex(@"(?<=\s+)([^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); internal static readonly Regex AttribNameValueRegex = new Regex(@"([^""'>/=\s\p{Cc}]+)(?:\s*=\s*(?:""(.*?)""|'(.*?)'|([^'"">\s]+)))?", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); internal static readonly Regex ImageRegex = new Regex(@"<img\s[^>]*?>", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); internal static readonly Regex InputFieldRegex = new Regex(@"<input\s+[^>]*(/?>|>.*?</input>)", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); internal static readonly Regex LinkRegex = new Regex(@"<a\s+[^>]*(/>|>.*?</a>)", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); internal static readonly Regex TagRegex = new Regex(@"<\w+((\s+[^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)+\s*|\s*)/?>", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); } } }