Skip to content

Commit e0fbd5b

Browse files
committed
GSF.Web: Introduce a media type formatter for byte arrays
1 parent ff82250 commit e0fbd5b

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed

Source/Libraries/GSF.Web/GSF.Web.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@
329329
<Reference Include="WindowsBase" />
330330
</ItemGroup>
331331
<ItemGroup>
332+
<Compile Include="Hosting\OctetStreamMediaTypeFormatter.cs" />
332333
<Compile Include="Model\ModelController.cs" />
333334
<Compile Include="ModuleInitializer.cs" />
334335
<Compile Include="Security\AntiForgery.cs" />
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//******************************************************************************************************
2+
// OctetStreamMediaTypeFormatter.cs - Gbtc
3+
//
4+
// Copyright © 2026, Grid Protection Alliance. All Rights Reserved.
5+
//
6+
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
9+
// file except in compliance with the License. You may obtain a copy of the License at:
10+
//
11+
// http://opensource.org/licenses/MIT
12+
//
13+
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+
// License for the specific language governing permissions and limitations.
16+
//
17+
// Code Modification History:
18+
// ----------------------------------------------------------------------------------------------------
19+
// 04/08/2026 - Stephen C. Wills
20+
// Generated original version of source code.
21+
//
22+
//******************************************************************************************************
23+
24+
using System;
25+
using System.Collections.Generic;
26+
using System.IO;
27+
using System.Linq;
28+
using System.Net;
29+
using System.Net.Http;
30+
using System.Net.Http.Formatting;
31+
using System.Net.Http.Headers;
32+
using System.Text;
33+
using System.Threading;
34+
using System.Threading.Tasks;
35+
36+
namespace GSF.Web.Hosting;
37+
38+
/// <summary>
39+
/// A <see cref="MediaTypeFormatter"/> for HTTP data as a byte array.
40+
/// </summary>
41+
public class OctetStreamMediaTypeFormatter : MediaTypeFormatter
42+
{
43+
/// <summary>
44+
/// Creates a new instance of the <see cref="OctetStreamMediaTypeFormatter"/> class.
45+
/// </summary>
46+
public OctetStreamMediaTypeFormatter()
47+
{
48+
const string ApplicationOctetStream = "application/octet-stream";
49+
MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue(ApplicationOctetStream);
50+
SupportedMediaTypes.Add(mediaType);
51+
}
52+
53+
/// <inheritdoc/>
54+
public override bool CanReadType(Type type)
55+
{
56+
return type == typeof(byte[]);
57+
}
58+
59+
/// <inheritdoc/>
60+
public override bool CanWriteType(Type type)
61+
{
62+
return type == typeof(byte[]);
63+
}
64+
65+
/// <inheritdoc/>
66+
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
67+
{
68+
return ReadFromStreamAsync(type, readStream, content, formatterLogger, default);
69+
}
70+
71+
/// <inheritdoc/>
72+
public override async Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
73+
{
74+
long length = content.Headers.ContentLength.GetValueOrDefault();
75+
76+
return (length > 0L && length <= int.MaxValue)
77+
? await ReadFromAsync(readStream, (int)length, cancellationToken)
78+
: await ReadFromAsync(readStream, cancellationToken);
79+
}
80+
81+
/// <inheritdoc/>
82+
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
83+
{
84+
return WriteToStreamAsync(type, value, writeStream, content, transportContext, default);
85+
}
86+
87+
/// <inheritdoc/>
88+
public override async Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)
89+
{
90+
if (!(value is byte[] bytes))
91+
throw new InvalidOperationException("Value is not a byte array");
92+
93+
await writeStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
94+
}
95+
96+
private async Task<byte[]> ReadFromAsync(Stream stream, int length, CancellationToken cancellationToken)
97+
{
98+
byte[] bytes = new byte[length];
99+
int offset = 0;
100+
int remaining = length;
101+
102+
while (offset < length)
103+
{
104+
int bytesRead = await stream.ReadAsync(bytes, offset, remaining, cancellationToken);
105+
if (bytesRead == 0) throw new EndOfStreamException();
106+
offset += bytesRead;
107+
remaining -= bytesRead;
108+
}
109+
110+
return bytes;
111+
}
112+
113+
private async Task<byte[]> ReadFromAsync(Stream stream, CancellationToken cancellationToken)
114+
{
115+
using (MemoryStream memoryStream = new MemoryStream())
116+
{
117+
await stream.CopyToAsync(memoryStream, 81920, cancellationToken);
118+
return memoryStream.ToArray();
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)