diff --git a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs index 31b00dafff..770963efe8 100644 --- a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs +++ b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs @@ -452,6 +452,14 @@ public System.Text.Json.JsonSerializerOptions UseSystemTextJsonSerializerWithOpt /// The default value is 'true'. /// internal bool EnableAsyncCacheExceptionNoSharing { get; set; } = true; + + /// + /// Gets or sets the boolean flag to skip converting a text stream to binary and vice versa. When enabled, the request and response stream + /// would not be converted to the desired target serialization type and will act just like a pass through. This client option will + /// remain internal only since the consumer of this flag will be the internal components of the cosmos db ecosystem. + /// The default value for this parameter is 'false'. + /// + internal bool EnableStreamPassThrough { get; set; } = false; /// /// (Direct/TCP) Controls the amount of idle time after which unused connections are closed. diff --git a/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.Items.cs b/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.Items.cs index af20ae5038..194963aab6 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.Items.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.Items.cs @@ -934,7 +934,8 @@ private async Task ProcessItemStreamAsync( // engine, which does not support binary encoded content at the moment. For long term, since trigger operations won't // be supported in the backend, avoiding the binary encoding in such cases, will be the ideal approach. if (ConfigurationManager.IsBinaryEncodingEnabled() - && !ContainerCore.IsTriggerPresentInRequestOptions(requestOptions)) + && !ContainerCore.IsTriggerPresentInRequestOptions(requestOptions) + && !this.ClientContext.ClientOptions.EnableStreamPassThrough) { streamPayload = CosmosSerializationUtil.TrySerializeStreamToTargetFormat( targetSerializationFormat: ContainerCore.GetTargetRequestSerializationFormat(), @@ -957,7 +958,8 @@ private async Task ProcessItemStreamAsync( cancellationToken: cancellationToken); // Convert Binary Stream to Text. - if (targetResponseSerializationFormat.HasValue + if (!this.ClientContext.ClientOptions.EnableStreamPassThrough + && targetResponseSerializationFormat.HasValue && (requestOptions == null || !requestOptions.EnableBinaryResponseOnPointOperations) && responseMessage?.Content is CloneableStream outputCloneableStream) { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs index 1504d4b077..0cf39af658 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs @@ -768,6 +768,138 @@ public async Task CreateDropItemStreamTest(bool binaryEncodingEnabledInClient, b { Environment.SetEnvironmentVariable(ConfigurationManager.BinaryEncodingEnabled, null); } + } + + [TestMethod] + [Owner("dkunda")] + [DataRow(true, true, DisplayName = "Test scenario when binary encoding is enabled at client level and stream conversation for binary encoding is skipped.")] + [DataRow(true, false, DisplayName = "Test scenario when binary encoding is enabled at client level and stream conversation for binary encoding is enabled.")] + [DataRow(false, true, DisplayName = "Test scenario when binary encoding is disabled at client level and stream conversation for binary encoding is skipped.")] + [DataRow(false, false, DisplayName = "Test scenario when binary encoding is disabled at client level and stream conversation for binary encoding is enabled.")] + public async Task CreateItemStream_WithEnableBinaryResponseOptions_ShouldSkipStreamConversation( + bool binaryEncodingEnabledInClient, + bool enableStreamPassThrough) + { + Cosmos.Database database = null; + Container container = null; + try + { + string databaseName = "binary-encoding-db"; + string containerName = "binary-encoding-container"; + if (binaryEncodingEnabledInClient) + { + Environment.SetEnvironmentVariable(ConfigurationManager.BinaryEncodingEnabled, "True"); + } + + (string endpoint, string authKey) = TestCommon.GetAccountInfo(); + CosmosClientOptions clientOptions = new CosmosClientOptions() + { + EnableStreamPassThrough = enableStreamPassThrough, + }; + + CosmosClient cosmosClient = new ( + endpoint, + authKey, + clientOptions); + + DatabaseResponse dbResponse = await cosmosClient.CreateDatabaseIfNotExistsAsync(databaseName); + database = dbResponse.Database; + + ContainerProperties properties = new (id: containerName, partitionKeyPath: "/pk"); + container = await database.CreateContainerIfNotExistsAsync(properties); + + ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity(); + CosmosSerializerCore cosmosSerializer = new CosmosSerializerCore(); + using (Stream stream = cosmosSerializer.ToStream( + testItem, + canUseBinaryEncodingForPointOperations: binaryEncodingEnabledInClient)) + { + if (binaryEncodingEnabledInClient) + { + // Asserting the input stream is in binary format. + AssertOnResponseSerializationBinaryType(stream); + } + else + { + // Asserting the input stream is in text format. + AssertOnResponseSerializationTextType(stream); + } + + using (ResponseMessage response = await container.CreateItemStreamAsync( + streamPayload: stream, + partitionKey: new Cosmos.PartitionKey(testItem.pk))) + { + Assert.IsNotNull(response); + Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); + Assert.IsTrue(response.Headers.RequestCharge > 0); + Assert.IsNotNull(response.Headers.ActivityId); + Assert.IsNotNull(response.Headers.ETag); + Assert.IsNotNull(response.Diagnostics); + Assert.IsTrue(!string.IsNullOrEmpty(response.Diagnostics.ToString())); + Assert.IsTrue(response.Diagnostics.GetClientElapsedTime() > TimeSpan.Zero); + + if (!enableStreamPassThrough) + { + AssertOnResponseSerializationTextType(response.Content); + } + else + { + if (binaryEncodingEnabledInClient) + { + AssertOnResponseSerializationBinaryType(response.Content); + } + else + { + AssertOnResponseSerializationTextType(response.Content); + } + } + } + } + + using (ResponseMessage response = await container.ReadItemStreamAsync( + id: testItem.id, + partitionKey: new Cosmos.PartitionKey(testItem.pk))) + { + Assert.IsNotNull(response); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + Assert.IsTrue(response.Headers.RequestCharge > 0); + Assert.IsNotNull(response.Headers.ActivityId); + Assert.IsNotNull(response.Headers.ETag); + Assert.IsNotNull(response.Diagnostics); + Assert.IsTrue(!string.IsNullOrEmpty(response.Diagnostics.ToString())); + Assert.IsTrue(response.Diagnostics.GetClientElapsedTime() > TimeSpan.Zero); + + if (!enableStreamPassThrough) + { + AssertOnResponseSerializationTextType(response.Content); + } + else + { + if (binaryEncodingEnabledInClient) + { + AssertOnResponseSerializationBinaryType(response.Content); + } + else + { + AssertOnResponseSerializationTextType(response.Content); + } + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.BinaryEncodingEnabled, null); + + if (container != null) + { + await container.DeleteContainerStreamAsync(); + } + + if (database != null) + { + await database.DeleteAsync(); + } + } } [TestMethod]