-
Notifications
You must be signed in to change notification settings - Fork 299
Add HTTP Response Compression Support #3003
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -828,6 +828,21 @@ private static bool TryUpdateConfiguredRuntimeOptions( | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| // Compression: Level | ||||||
| if (options.RuntimeCompressionLevel != null) | ||||||
| { | ||||||
| CompressionOptions updatedCompressionOptions = runtimeConfig?.Runtime?.Compression ?? new(); | ||||||
| bool status = TryUpdateConfiguredCompressionValues(options, ref updatedCompressionOptions); | ||||||
| if (status) | ||||||
| { | ||||||
| runtimeConfig = runtimeConfig! with { Runtime = runtimeConfig.Runtime! with { Compression = updatedCompressionOptions } }; | ||||||
| } | ||||||
| else | ||||||
| { | ||||||
| return false; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| // Host: Mode, Cors.Origins, Cors.AllowCredentials, Authentication.Provider, Authentication.Jwt.Audience, Authentication.Jwt.Issuer | ||||||
| if (options.RuntimeHostMode != null || | ||||||
| options.RuntimeHostCorsOrigins != null || | ||||||
|
|
@@ -1197,6 +1212,37 @@ private static bool TryUpdateConfiguredCacheValues( | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Attempts to update the Config parameters in the Compression runtime settings based on the provided value. | ||||||
| /// Validates user-provided parameters and then returns true if the updated Compression options | ||||||
| /// need to be overwritten on the existing config parameters. | ||||||
| /// </summary> | ||||||
| /// <param name="options">options.</param> | ||||||
| /// <param name="updatedCompressionOptions">updatedCompressionOptions.</param> | ||||||
| /// <returns>True if the value needs to be updated in the runtime config, else false</returns> | ||||||
| private static bool TryUpdateConfiguredCompressionValues( | ||||||
| ConfigureOptions options, | ||||||
| ref CompressionOptions updatedCompressionOptions) | ||||||
| { | ||||||
| try | ||||||
| { | ||||||
| // Runtime.Compression.Level | ||||||
| CompressionLevel? updatedValue = options?.RuntimeCompressionLevel; | ||||||
| if (updatedValue != null) | ||||||
| { | ||||||
| updatedCompressionOptions = updatedCompressionOptions with { Level = updatedValue.Value, UserProvidedLevel = true }; | ||||||
| _logger.LogInformation("Updated RuntimeConfig with Runtime.Compression.Level as '{updatedValue}'", updatedValue); | ||||||
| } | ||||||
|
|
||||||
| return true; | ||||||
| } | ||||||
| catch (Exception ex) | ||||||
| { | ||||||
| _logger.LogError("Failed to update RuntimeConfig.Compression with exception message: {exceptionMessage}.", ex.Message); | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| return false; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Attempts to update the Config parameters in the Host runtime settings based on the provided value. | ||||||
| /// Validates that any user-provided parameter value is valid and then returns true if the updated Host options | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,99 @@ | ||||||||||||||
| // Copyright (c) Microsoft Corporation. | ||||||||||||||
| // Licensed under the MIT License. | ||||||||||||||
|
|
||||||||||||||
| using System.Text.Json; | ||||||||||||||
| using System.Text.Json.Serialization; | ||||||||||||||
| using Azure.DataApiBuilder.Config.ObjectModel; | ||||||||||||||
|
|
||||||||||||||
| namespace Azure.DataApiBuilder.Config.Converters; | ||||||||||||||
|
|
||||||||||||||
| /// <summary> | ||||||||||||||
| /// Defines how DAB reads and writes the compression options (JSON). | ||||||||||||||
| /// </summary> | ||||||||||||||
| internal class CompressionOptionsConverterFactory : JsonConverterFactory | ||||||||||||||
| { | ||||||||||||||
| /// <inheritdoc/> | ||||||||||||||
| public override bool CanConvert(Type typeToConvert) | ||||||||||||||
| { | ||||||||||||||
| return typeToConvert.IsAssignableTo(typeof(CompressionOptions)); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /// <inheritdoc/> | ||||||||||||||
| public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) | ||||||||||||||
| { | ||||||||||||||
| return new CompressionOptionsConverter(); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| private class CompressionOptionsConverter : JsonConverter<CompressionOptions> | ||||||||||||||
| { | ||||||||||||||
| /// <summary> | ||||||||||||||
| /// Defines how DAB reads the compression options and defines which values are | ||||||||||||||
| /// used to instantiate CompressionOptions. | ||||||||||||||
| /// </summary> | ||||||||||||||
| public override CompressionOptions? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | ||||||||||||||
| { | ||||||||||||||
| if (reader.TokenType == JsonTokenType.Null) | ||||||||||||||
| { | ||||||||||||||
| return null; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| if (reader.TokenType != JsonTokenType.StartObject) | ||||||||||||||
| { | ||||||||||||||
| throw new JsonException("Expected start of object."); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| CompressionLevel level = CompressionOptions.DEFAULT_LEVEL; | ||||||||||||||
| bool userProvidedLevel = false; | ||||||||||||||
|
|
||||||||||||||
| while (reader.Read()) | ||||||||||||||
| { | ||||||||||||||
| if (reader.TokenType == JsonTokenType.EndObject) | ||||||||||||||
| { | ||||||||||||||
| break; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| if (reader.TokenType == JsonTokenType.PropertyName) | ||||||||||||||
| { | ||||||||||||||
| string? propertyName = reader.GetString(); | ||||||||||||||
| reader.Read(); | ||||||||||||||
|
|
||||||||||||||
| if (string.Equals(propertyName, "level", StringComparison.OrdinalIgnoreCase)) | ||||||||||||||
| { | ||||||||||||||
| string? levelStr = reader.GetString(); | ||||||||||||||
| if (levelStr is not null) | ||||||||||||||
| { | ||||||||||||||
| if (Enum.TryParse<CompressionLevel>(levelStr, ignoreCase: true, out CompressionLevel parsedLevel)) | ||||||||||||||
| { | ||||||||||||||
| level = parsedLevel; | ||||||||||||||
| userProvidedLevel = true; | ||||||||||||||
| } | ||||||||||||||
| else | ||||||||||||||
| { | ||||||||||||||
| throw new JsonException($"Invalid compression level: '{levelStr}'. Valid values are: optimal, fastest, none."); | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
||||||||||||||
| } | |
| else | |
| { | |
| // Skip unknown properties and their values (including objects/arrays) | |
| reader.Skip(); | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| using System.Text.Json.Serialization; | ||
|
|
||
| namespace Azure.DataApiBuilder.Config.ObjectModel; | ||
|
|
||
| /// <summary> | ||
| /// Specifies the compression level for HTTP response compression. | ||
| /// </summary> | ||
| [JsonConverter(typeof(JsonStringEnumConverter))] | ||
| public enum CompressionLevel | ||
| { | ||
| /// <summary> | ||
| /// Provides the best compression ratio at the cost of speed. | ||
| /// </summary> | ||
| Optimal, | ||
|
|
||
| /// <summary> | ||
| /// Provides the fastest compression at the cost of compression ratio. | ||
| /// </summary> | ||
| Fastest, | ||
|
|
||
| /// <summary> | ||
| /// Disables compression. | ||
| /// </summary> | ||
| None | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| using System.Text.Json.Serialization; | ||
|
|
||
| namespace Azure.DataApiBuilder.Config.ObjectModel; | ||
|
|
||
| /// <summary> | ||
| /// Configuration options for HTTP response compression. | ||
| /// </summary> | ||
| public record CompressionOptions | ||
| { | ||
| /// <summary> | ||
| /// Default compression level is Optimal. | ||
| /// </summary> | ||
| public const CompressionLevel DEFAULT_LEVEL = CompressionLevel.Optimal; | ||
|
|
||
| /// <summary> | ||
| /// The compression level to use for HTTP response compression. | ||
| /// </summary> | ||
| [JsonPropertyName("level")] | ||
| public CompressionLevel Level { get; init; } = DEFAULT_LEVEL; | ||
|
|
||
| /// <summary> | ||
| /// Flag which informs CLI and JSON serializer whether to write Level | ||
| /// property and value to the runtime config file. | ||
| /// </summary> | ||
| [JsonIgnore(Condition = JsonIgnoreCondition.Always)] | ||
| public bool UserProvidedLevel { get; init; } = false; | ||
|
|
||
| [JsonConstructor] | ||
| public CompressionOptions(CompressionLevel Level = DEFAULT_LEVEL) | ||
| { | ||
| this.Level = Level; | ||
| this.UserProvidedLevel = true; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Default parameterless constructor for cases where no compression level is specified. | ||
| /// </summary> | ||
| public CompressionOptions() | ||
| { | ||
| this.Level = DEFAULT_LEVEL; | ||
| this.UserProvidedLevel = false; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing closing period in the documentation comment. The summary should end with a period for consistency with other test documentation in the file.