diff --git a/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs b/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs index da178ae86..732708459 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. +// Licensed under the MIT license. using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; @@ -61,11 +61,17 @@ public class OpenApiReaderSettings public Uri BaseUrl { get; set; } /// - /// Function used to provide an alternative loader for accessing external references. + /// Function used to provide an alternative loader for accessing external references. /// /// /// Default loader will attempt to dereference http(s) urls and file urls. /// public IStreamLoader CustomExternalLoader { get; set; } + + /// + /// Whether to leave the object open after reading + /// from an object. + /// + public bool LeaveStreamOpen { get; set; } } } diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs index cab5d1a83..cccf06a68 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. +// Licensed under the MIT license. using System.IO; using System.Threading.Tasks; @@ -29,14 +29,18 @@ public OpenApiStreamReader(OpenApiReaderSettings settings = null) /// Reads the stream input and parses it into an Open API document. /// /// Stream containing OpenAPI description to parse. - /// Returns diagnostic object containing errors detected during parsing - /// Instance of newly created OpenApiDocument + /// Returns diagnostic object containing errors detected during parsing. + /// Instance of newly created OpenApiDocument. public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic) { - using (var reader = new StreamReader(input)) + var reader = new StreamReader(input); + var result = new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); + if (!_settings.LeaveStreamOpen) { - return new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); + reader.Dispose(); } + + return result; } /// @@ -50,8 +54,8 @@ public async Task ReadAsync(Stream input) if (input is MemoryStream) { bufferedStream = (MemoryStream)input; - } - else + } + else { // Buffer stream so that OpenApiTextReaderReader can process it synchronously // YamlDocument doesn't support async reading. diff --git a/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs b/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs new file mode 100644 index 000000000..7567e0b7d --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.IO; +using Xunit; + +namespace Microsoft.OpenApi.Readers.Tests.OpenApiReaderTests +{ + public class OpenApiStreamReaderTests + { + private const string SampleFolderPath = "V3Tests/Samples/OpenApiDocument/"; + + [Fact] + public void StreamShouldCloseIfLeaveStreamOpenSettingEqualsFalse() + { + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "petStore.yaml"))) + { + var reader = new OpenApiStreamReader(new OpenApiReaderSettings { LeaveStreamOpen = false }); + reader.Read(stream, out _); + Assert.False(stream.CanRead); + } + } + + [Fact] + public void StreamShouldNotCloseIfLeaveStreamOpenSettingEqualsTrue() + { + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "petStore.yaml"))) + { + var reader = new OpenApiStreamReader(new OpenApiReaderSettings { LeaveStreamOpen = true}); + reader.Read(stream, out _); + Assert.True(stream.CanRead); + } + } + } +}