openai/openai-dotnet

Public

mirrored from https://github.com/openai/openai-dotnetAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
OpenAI_2.0.0-beta.7

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/Utility/MultipartFormDataBinaryContent.cs

170lines · modeblame

9f9f2936Jose Arriaga Maldonado2 years ago1using System;
2using System.ClientModel;
3using System.Diagnostics;
4using System.Globalization;
5using System.IO;
6using System.Net.Http;
7using System.Net.Http.Headers;
8using System.Threading;
9using System.Threading.Tasks;
10
11namespace OpenAI;
12
13internal class MultipartFormDataBinaryContent : BinaryContent
14{
15private readonly MultipartFormDataContent _multipartContent;
16
7cacdee2Stephen Toub2 years ago17private const int BoundaryLength = 70;
18private const string BoundaryValues = "0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
9f9f2936Jose Arriaga Maldonado2 years ago19
20public MultipartFormDataBinaryContent()
21{
22_multipartContent = new MultipartFormDataContent(CreateBoundary());
23}
24
25public string ContentType
26{
27get
28{
29Debug.Assert(_multipartContent.Headers.ContentType is not null);
30
31return _multipartContent.Headers.ContentType!.ToString();
32}
33}
34
35internal HttpContent HttpContent => _multipartContent;
36
2ba8e695Travis Wilson2 years ago37public void Add(Stream stream, string name, string fileName = default, string contentType = null)
9f9f2936Jose Arriaga Maldonado2 years ago38{
2ba8e695Travis Wilson2 years ago39Argument.AssertNotNull(stream, nameof(stream));
9f9f2936Jose Arriaga Maldonado2 years ago40
2ba8e695Travis Wilson2 years ago41StreamContent content = new(stream);
42if (contentType is not null)
43{
44content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType);
45}
46Add(content, name, fileName);
9f9f2936Jose Arriaga Maldonado2 years ago47}
48
49public void Add(string content, string name, string fileName = default)
50{
51Add(new StringContent(content), name, fileName);
52}
53
54public void Add(int content, string name, string fileName = default)
55{
56// https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#GFormatString
57string value = content.ToString("G", CultureInfo.InvariantCulture);
58Add(new StringContent(value), name, fileName);
59}
60
61public void Add(double content, string name, string fileName = default)
62{
63// https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#GFormatString
64string value = content.ToString("G", CultureInfo.InvariantCulture);
65Add(new StringContent(value), name, fileName);
66}
67
68public void Add(byte[] content, string name, string fileName = default)
69{
70Add(new ByteArrayContent(content), name, fileName);
71}
72
73public void Add(BinaryData content, string name, string fileName = default)
74{
75Add(new ByteArrayContent(content.ToArray()), name, fileName);
76}
77
78private void Add(HttpContent content, string name, string fileName)
79{
2ba8e695Travis Wilson2 years ago80Argument.AssertNotNull(content, nameof(content));
81Argument.AssertNotNull(name, nameof(name));
82
9f9f2936Jose Arriaga Maldonado2 years ago83if (fileName is not null)
84{
2ba8e695Travis Wilson2 years ago85_multipartContent.Add(content, name, fileName);
9f9f2936Jose Arriaga Maldonado2 years ago86}
2ba8e695Travis Wilson2 years ago87else
9f9f2936Jose Arriaga Maldonado2 years ago88{
2ba8e695Travis Wilson2 years ago89_multipartContent.Add(content, name);
90}
9f9f2936Jose Arriaga Maldonado2 years ago91}
92
7cacdee2Stephen Toub2 years ago93#if NET6_0_OR_GREATER
94private static string CreateBoundary() =>
95string.Create(BoundaryLength, 0, (chars, _) =>
96{
97Span<byte> random = stackalloc byte[BoundaryLength];
98Random.Shared.NextBytes(random);
99
100for (int i = 0; i < chars.Length; i++)
101{
102chars[i] = BoundaryValues[random[i] % BoundaryValues.Length];
103}
104});
105#else
106private static readonly Random _random = new();
107
9f9f2936Jose Arriaga Maldonado2 years ago108private static string CreateBoundary()
109{
7cacdee2Stephen Toub2 years ago110Span<char> chars = stackalloc char[BoundaryLength];
9f9f2936Jose Arriaga Maldonado2 years ago111
7cacdee2Stephen Toub2 years ago112byte[] random = new byte[BoundaryLength];
113lock (_random)
114{
115_random.NextBytes(random);
116}
9f9f2936Jose Arriaga Maldonado2 years ago117
7cacdee2Stephen Toub2 years ago118// Instead of `% BoundaryValues.Length` as is used above, use a mask to achieve the same result.
119// `% BoundaryValues.Length` is optimized to the equivalent on .NET Core but not on .NET Framework.
120const int Mask = 255 >> 2;
121Debug.Assert(BoundaryValues.Length - 1 == Mask);
9f9f2936Jose Arriaga Maldonado2 years ago122
7cacdee2Stephen Toub2 years ago123for (int i = 0; i < chars.Length; i++)
9f9f2936Jose Arriaga Maldonado2 years ago124{
7cacdee2Stephen Toub2 years ago125chars[i] = BoundaryValues[random[i] & Mask];
9f9f2936Jose Arriaga Maldonado2 years ago126}
127
128return chars.ToString();
129}
7cacdee2Stephen Toub2 years ago130#endif
9f9f2936Jose Arriaga Maldonado2 years ago131
132public override bool TryComputeLength(out long length)
133{
134// We can't call the protected method on HttpContent
135
136if (_multipartContent.Headers.ContentLength is long contentLength)
137{
138length = contentLength;
139return true;
140}
141
142length = 0;
143return false;
144}
145
146public override void WriteTo(Stream stream, CancellationToken cancellationToken = default)
147{
7cacdee2Stephen Toub2 years ago148#if NET5_0_OR_GREATER
9f9f2936Jose Arriaga Maldonado2 years ago149_multipartContent.CopyTo(stream, default, cancellationToken);
150#else
7cacdee2Stephen Toub2 years ago151// TODO: polyfill sync-over-async for netstandard2.0 for Azure clients.
152// Tracked by https://github.com/Azure/azure-sdk-for-net/issues/42674
153_multipartContent.CopyToAsync(stream).GetAwaiter().GetResult();
9f9f2936Jose Arriaga Maldonado2 years ago154#endif
155}
156
157public override async Task WriteToAsync(Stream stream, CancellationToken cancellationToken = default)
158{
7cacdee2Stephen Toub2 years ago159#if NET5_0_OR_GREATER
9f9f2936Jose Arriaga Maldonado2 years ago160await _multipartContent.CopyToAsync(stream, cancellationToken).ConfigureAwait(false);
161#else
7cacdee2Stephen Toub2 years ago162await _multipartContent.CopyToAsync(stream).ConfigureAwait(false);
9f9f2936Jose Arriaga Maldonado2 years ago163#endif
164}
165
166public override void Dispose()
167{
168_multipartContent.Dispose();
169}
170}