@@ -51,22 +51,10 @@ public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings
5151 public static async Task < ReadResult > LoadAsync ( Stream input , string format = null , OpenApiReaderSettings settings = null , CancellationToken cancellationToken = default )
5252 {
5353 settings ??= new OpenApiReaderSettings ( ) ;
54- format ??= InspectStreamFormat ( input ) ;
5554
56- Stream preparedStream ;
57-
58- // Avoid buffering for JSON documents
59- if ( input is MemoryStream || format . Equals ( OpenApiConstants . Json , StringComparison . OrdinalIgnoreCase ) )
60- {
61- preparedStream = input ;
62- }
63- else
64- {
65- // Buffer stream for non-JSON formats (e.g., YAML) since they require synchronous reading
66- preparedStream = new MemoryStream ( ) ;
67- await input . CopyToAsync ( preparedStream , 81920 , cancellationToken ) ;
68- preparedStream . Position = 0 ;
69- }
55+ // Prepare the stream based on seekability and format
56+ var ( preparedStream , detectedFormat ) = await PrepareStreamForReadingAsync ( input , format , cancellationToken ) ;
57+ format ??= detectedFormat ;
7058
7159 // Use StreamReader to process the prepared stream (buffered for YAML, direct for JSON)
7260 using var reader = new StreamReader ( preparedStream , default , true , - 1 , settings . LeaveStreamOpen ) ;
@@ -231,49 +219,86 @@ public static async Task<string> GetFormatAsync(string url)
231219 return null ;
232220 }
233221
234- private static string InspectStreamFormat ( Stream stream )
222+ private static async Task < ( Stream preparedStream , string format ) > PrepareStreamForReadingAsync ( Stream input , string format , CancellationToken token = default )
235223 {
236- try
224+ Stream preparedStream = input ;
225+
226+ if ( ! input . CanSeek )
237227 {
238- if ( stream == null ) throw new ArgumentNullException ( nameof ( stream ) ) ;
239- if ( ! stream . CanSeek ) throw new InvalidOperationException ( "Stream must support seeking." ) ; // ensure stream supports seeking to reset position
228+ // Use a temporary buffer to read a small portion for format detection
229+ using var bufferStream = new MemoryStream ( ) ;
230+ await input . CopyToAsync ( bufferStream , 1024 , token ) ;
231+ bufferStream . Position = 0 ;
240232
241- long initialPosition = stream . Position ;
242- int firstByte = stream . ReadByte ( ) ;
233+ // Inspect the format from the buffered portion
234+ format ??= InspectStreamFormat ( bufferStream ) ;
243235
244- // Check if stream is empty or contains only whitespace
245- if ( firstByte == - 1 )
236+ // If format is JSON, no need to buffer further — use the original stream.
237+ if ( format . Equals ( OpenApiConstants . Json , StringComparison . OrdinalIgnoreCase ) )
246238 {
247- stream . Position = initialPosition ;
248- throw new InvalidOperationException ( "Stream is empty or contains only whitespace." ) ;
239+ preparedStream = input ;
249240 }
250-
251- // Skip whitespace if present and read the next non-whitespace byte
252- if ( char . IsWhiteSpace ( ( char ) firstByte ) )
241+ else
253242 {
254- firstByte = stream . ReadByte ( ) ;
243+ // YAML or other non-JSON format; copy remaining input to a new stream.
244+ preparedStream = new MemoryStream ( ) ;
245+ bufferStream . Position = 0 ;
246+ await bufferStream . CopyToAsync ( preparedStream , 81920 , token ) ; // Copy buffered portion
247+ await input . CopyToAsync ( preparedStream , 81920 , token ) ; // Copy remaining data
248+ preparedStream . Position = 0 ;
249+ }
250+ }
251+ else
252+ {
253+ format ??= InspectStreamFormat ( input ) ;
255254
256- // If still whitespace or end of stream, throw an error
257- if ( firstByte == - 1 || char . IsWhiteSpace ( ( char ) firstByte ) )
258- {
259- stream . Position = initialPosition ;
260- throw new InvalidOperationException ( "Stream is empty or contains only whitespace." ) ;
261- }
255+ if ( ! format . Equals ( OpenApiConstants . Json , StringComparison . OrdinalIgnoreCase ) )
256+ {
257+ // Buffer stream for non-JSON formats (e.g., YAML) since they require synchronous reading
258+ preparedStream = new MemoryStream ( ) ;
259+ await input . CopyToAsync ( preparedStream , 81920 , token ) ;
260+ preparedStream . Position = 0 ;
262261 }
262+ }
263+
264+ return ( preparedStream , format ) ;
265+ }
266+
267+ private static string InspectStreamFormat ( Stream stream )
268+ {
269+ if ( stream == null ) throw new ArgumentNullException ( nameof ( stream ) ) ;
270+
271+ long initialPosition = stream . Position ;
272+ int firstByte = stream . ReadByte ( ) ;
263273
264- stream . Position = initialPosition ; // Reset the stream position to the beginning
274+ // Check if stream is empty or contains only whitespace
275+ if ( firstByte == - 1 )
276+ {
277+ stream . Position = initialPosition ;
278+ throw new InvalidOperationException ( "Stream is empty or contains only whitespace." ) ;
279+ }
265280
266- char firstChar = ( char ) firstByte ;
267- return firstChar switch
281+ // Skip whitespace if present and read the next non-whitespace byte
282+ if ( char . IsWhiteSpace ( ( char ) firstByte ) )
283+ {
284+ firstByte = stream . ReadByte ( ) ;
285+
286+ // If still whitespace or end of stream, throw an error
287+ if ( firstByte == - 1 || char . IsWhiteSpace ( ( char ) firstByte ) )
268288 {
269- '{' or '[' => OpenApiConstants . Json , // If the first character is '{' or '[', assume JSON
270- _ => OpenApiConstants . Yaml // Otherwise assume YAML
271- } ;
289+ stream . Position = initialPosition ;
290+ throw new InvalidOperationException ( "Stream is empty or contains only whitespace." ) ;
291+ }
272292 }
273- catch ( Exception ex )
293+
294+ stream . Position = initialPosition ; // Reset the stream position to the beginning
295+
296+ char firstChar = ( char ) firstByte ;
297+ return firstChar switch
274298 {
275- throw new OpenApiException ( ex . Message ) ;
276- }
299+ '{' or '[' => OpenApiConstants . Json , // If the first character is '{' or '[', assume JSON
300+ _ => OpenApiConstants . Yaml // Otherwise assume YAML
301+ } ;
277302 }
278303
279304 private static string InspectTextReaderFormat ( TextReader reader )
0 commit comments