| File: System\Net\Http\FormUrlEncodedContent.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.Http\src\System.Net.Http.csproj (System.Net.Http) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.IO; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; namespace System.Net.Http { public class FormUrlEncodedContent : ByteArrayContent { public FormUrlEncodedContent( IEnumerable<KeyValuePair< #nullable disable string, string #nullable restore >> nameValueCollection) : base(GetContentByteArray(nameValueCollection)) { Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); } private static byte[] GetContentByteArray(IEnumerable<KeyValuePair<string?, string?>> nameValueCollection) { ArgumentNullException.ThrowIfNull(nameValueCollection); // Encode and concatenate data var builder = new ValueStringBuilder(stackalloc char[256]); foreach (KeyValuePair<string?, string?> pair in nameValueCollection) { if (builder.Length > 0) { builder.Append('&'); } Encode(ref builder, pair.Key); builder.Append('='); Encode(ref builder, pair.Value); } // EscapeDataString will always return an ASCII string and DefaultHttpEncoding is Latin1, // so we know the output byte size will be the same as the builder length. byte[] bytes = new byte[builder.Length]; HttpRuleParser.DefaultHttpEncoding.GetBytes(builder.AsSpan(), bytes); builder.Dispose(); return bytes; } private static void Encode(ref ValueStringBuilder builder, string? data) { if (!string.IsNullOrEmpty(data)) { int charsWritten; while (!Uri.TryEscapeDataString(data, builder.RawChars.Slice(builder.Length), out charsWritten)) { builder.EnsureCapacity(builder.Capacity + 1); } // Escape spaces as '+'. if (data.Contains(' ')) { ReadOnlySpan<char> escapedChars = builder.RawChars.Slice(builder.Length, charsWritten); while (true) { int indexOfEscapedSpace = escapedChars.IndexOf("%20", StringComparison.Ordinal); if (indexOfEscapedSpace < 0) { builder.Append(escapedChars); break; } builder.Append(escapedChars.Slice(0, indexOfEscapedSpace)); builder.Append('+'); escapedChars = escapedChars.Slice(indexOfEscapedSpace + 3); // Skip "%20" } } else { builder.Length += charsWritten; } } } protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken) => // Only skip the original protected virtual SerializeToStreamAsync if this // isn't a derived type that may have overridden the behavior. GetType() == typeof(FormUrlEncodedContent) ? SerializeToStreamAsyncCore(stream, cancellationToken) : base.SerializeToStreamAsync(stream, context, cancellationToken); internal override Stream? TryCreateContentReadStream() => GetType() == typeof(FormUrlEncodedContent) ? CreateMemoryStreamForByteArray() : // type check ensures we use possible derived type's CreateContentReadStreamAsync override null; } }