diff --git a/.circleci/config.yml b/.circleci/config.yml
index cbdcf7c9d..114d2b2e1 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,29 +1,30 @@
version: 2.1
orbs:
- queue: eddiewebb/queue@2.2.1
+ queue: eddiewebb/queue@3.1.4
jobs:
build:
docker:
- image: ocelot2/circleci-build:latest
+ resource_class: medium+
steps:
- checkout
- - run: dotnet tool restore && dotnet cake
+ - run: dotnet dev-certs https && dotnet tool restore && dotnet cake
release:
docker:
- image: ocelot2/circleci-build:latest
steps:
- checkout
- - run: dotnet tool restore && dotnet cake --target=Release
+ - run: dotnet dev-certs https && dotnet tool restore && dotnet cake --target=Release
workflows:
version: 2
main:
jobs:
- - queue/block_workflow:
- time: '20'
- only-on-branch: main
+ # - queue/block_workflow:
+ # time: '20'
+ # only-on-branch: main
- release:
- requires:
- - queue/block_workflow
+ # requires:
+ # - queue/block_workflow
filters:
branches:
only: main
@@ -33,7 +34,7 @@ workflows:
filters:
branches:
only: develop
- pr:
+ PR:
jobs:
- build:
filters:
diff --git a/.editorconfig b/.editorconfig
index e4e769b52..e8766a5e0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,15 +1,246 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
-[*]
-end_of_line = lf
-insert_final_newline = true
+# XML files
+[*.xml]
+indent_style = space
+indent_size = 2
+# C# files
[*.cs]
-end_of_line = lf
-indent_style = space
-indent_size = 4
-# XML files
-[*.xml]
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
indent_style = space
-indent_size = 2
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = true
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = false
+file_header_template = unset
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false
+dotnet_style_qualification_for_field = false
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_property = false
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true
+dotnet_style_predefined_type_for_member_access = true
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true
+dotnet_style_collection_initializer = true
+dotnet_style_explicit_tuple_names = true
+dotnet_style_namespace_match_folder = true
+dotnet_style_null_propagation = true
+dotnet_style_object_initializer = true
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true
+dotnet_style_prefer_collection_expression = false:suggestion
+dotnet_style_prefer_compound_assignment = true
+dotnet_style_prefer_conditional_expression_over_assignment = true
+dotnet_style_prefer_conditional_expression_over_return = true
+dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
+dotnet_style_prefer_inferred_anonymous_type_member_names = true
+dotnet_style_prefer_inferred_tuple_names = true
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true
+dotnet_style_prefer_simplified_boolean_expressions = true
+dotnet_style_prefer_simplified_interpolation = true
+
+# Field preferences
+dotnet_style_readonly_field = true
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+# New line preferences
+dotnet_style_allow_multiple_blank_lines_experimental = true
+dotnet_style_allow_statement_immediately_after_block_experimental = true
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = false
+csharp_style_var_for_built_in_types = false
+csharp_style_var_when_type_is_apparent = false
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true
+csharp_style_pattern_matching_over_is_with_cast_check = true
+csharp_style_prefer_extended_property_pattern = true
+csharp_style_prefer_not_pattern = true
+csharp_style_prefer_pattern_matching = true
+csharp_style_prefer_switch_expression = true
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true
+
+# Modifier preferences
+csharp_prefer_static_local_function = true
+csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
+csharp_style_prefer_readonly_struct = true
+csharp_style_prefer_readonly_struct_member = true
+
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_style_namespace_declarations = block_scoped:silent
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_primary_constructors = false:suggestion
+csharp_style_prefer_top_level_statements = true:silent
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true
+csharp_style_deconstructed_variable_declaration = true
+csharp_style_implicit_object_creation_when_type_is_apparent = true
+csharp_style_inlined_variable_declaration = true
+csharp_style_prefer_index_operator = true
+csharp_style_prefer_local_over_anonymous_function = true
+csharp_style_prefer_null_check_over_type_check = true
+csharp_style_prefer_range_operator = true
+csharp_style_prefer_tuple_swap = true
+csharp_style_prefer_utf8_string_literals = true
+csharp_style_throw_expression = true
+csharp_style_unused_value_assignment_preference = discard_variable
+csharp_style_unused_value_expression_statement_preference = discard_variable
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
+
+# New line preferences
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
+csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
+csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
+csharp_style_allow_embedded_statements_on_same_line_experimental = true
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+[*.{cs,vb}]
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+tab_width = 4
+indent_size = 4
+end_of_line = crlf
+dotnet_style_coalesce_expression = true:suggestion
+insert_final_newline = true
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
diff --git a/.gitignore b/.gitignore
index 1c30928ce..f01a1095e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -417,5 +417,4 @@ test/Ocelot.AcceptanceTests/ocelot.json
# Read the Docs
# https://ocelot.readthedocs.io
_build/
-_static/
_templates/
diff --git a/Directory.Build.props b/Directory.Build.props
deleted file mode 100644
index 4fe1ec98f..000000000
--- a/Directory.Build.props
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
- latest
- git
- https://github.com/ThreeMammals/Ocelot
-
- true
-
- true
- snupkg
-
-
-
-
-
diff --git a/Ocelot.Release.sln b/Ocelot.Release.sln
new file mode 100644
index 000000000..20a76ee83
--- /dev/null
+++ b/Ocelot.Release.sln
@@ -0,0 +1,231 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34728.123
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FA7C349-DBE8-4904-A2CE-015B8869CE6C}"
+ ProjectSection(SolutionItems) = preProject
+ .dockerignore = .dockerignore
+ .editorconfig = .editorconfig
+ .gitignore = .gitignore
+ .readthedocs.yaml = .readthedocs.yaml
+ build.cake = build.cake
+ build.ps1 = build.ps1
+ codeanalysis.ruleset = codeanalysis.ruleset
+ .circleci\config.yml = .circleci\config.yml
+ GitVersion.yml = GitVersion.yml
+ LICENSE.md = LICENSE.md
+ README.md = README.md
+ ReleaseNotes.md = ReleaseNotes.md
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5B401523-36DA-4491-B73A-7590A26E420B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.UnitTests", "test\Ocelot.UnitTests\Ocelot.UnitTests.csproj", "{54E84F1A-E525-4443-96EC-039CBD50C263}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.AcceptanceTests", "test\Ocelot.AcceptanceTests\Ocelot.AcceptanceTests.csproj", "{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.ManualTest", "test\Ocelot.ManualTest\Ocelot.ManualTest.csproj", "{02BBF4C5-517E-4157-8D21-4B8B9E118B7A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Benchmarks", "test\Ocelot.Benchmarks\Ocelot.Benchmarks.csproj", "{106B49E6-95F6-4A7B-B81C-96BFA74AF035}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.IntegrationTests", "test\Ocelot.IntegrationTests\Ocelot.IntegrationTests.csproj", "{D4575572-99CA-4530-8737-C296EDA326F8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Testing", "test\Ocelot.Testing\Ocelot.Testing.csproj", "{AE6BCCBD-0687-4C58-B30F-4ABBC6422087}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot", "src\Ocelot\Ocelot.csproj", "{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Administration", "src\Ocelot.Administration\Ocelot.Administration.csproj", "{F69CEF43-27D2-4940-A47A-FCA879E371BC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Cache.CacheManager", "src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj", "{EB9F438F-062E-499F-B6EA-4412BEF6D74C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Consul", "src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj", "{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Eureka", "src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj", "{9BBD3586-145C-4FA0-91C5-9ED58287D753}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Kubernetes", "src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{72C8E528-B4F5-45CE-8A06-CD3787364856}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Polly", "src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj", "{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.OpenTracing", "src\Ocelot.Tracing.OpenTracing\Ocelot.Tracing.OpenTracing.csproj", "{11C622AD-8C0A-4CF4-811B-3DBB76550797}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "samples", "{8FA0CBA0-0338-48EB-B37F-83CA5022237C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.AdministrationApi", "samples\Administration\Ocelot.Samples.AdministrationApi.csproj", "{A7F0CAFA-AECB-43CA-BE89-5F5B728E7C22}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.Basic.ApiGateway", "samples\Basic\Ocelot.Samples.Basic.ApiGateway.csproj", "{F00C73F4-019D-490D-8194-CA1754D717FA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.Eureka.ApiGateway", "samples\Eureka\ApiGateway\Ocelot.Samples.Eureka.ApiGateway.csproj", "{FECB0C8B-5778-4441-B10E-0C815F5106D5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.Eureka.DownstreamService", "samples\Eureka\DownstreamService\Ocelot.Samples.Eureka.DownstreamService.csproj", "{28AD7065-8DB1-4711-83BF-9EA47D75F8F7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.GraphQL", "samples\GraphQL\Ocelot.Samples.GraphQL.csproj", "{869EE931-7E4A-40AA-ADDD-D20DF34C3BB3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.Kubernetes.ApiGateway", "samples\Kubernetes\ApiGateway\Ocelot.Samples.Kubernetes.ApiGateway.csproj", "{681B6E08-114D-4B9B-8F82-E370CA29B8EC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.Kubernetes.DownstreamService", "samples\Kubernetes\DownstreamService\Ocelot.Samples.Kubernetes.DownstreamService.csproj", "{161DD558-993D-491B-AD20-966127D71E49}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.OpenTracing", "samples\OpenTracing\Ocelot.Samples.OpenTracing.csproj", "{DF9EFF21-58D3-428D-8A33-ACFA24E9B6E8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceDiscovery.ApiGateway", "samples\ServiceDiscovery\ApiGateway\Ocelot.Samples.ServiceDiscovery.ApiGateway.csproj", "{F25EA682-A763-431B-9D88-012A388D3618}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceDiscovery.DownstreamService", "samples\ServiceDiscovery\DownstreamService\Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj", "{DCBD0AB5-85DD-4F28-9166-0A23969E19EC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceFabric.ApiGateway", "samples\ServiceFabric\ApiGateway\Ocelot.Samples.ServiceFabric.ApiGateway.csproj", "{D991C694-01F0-4F04-8135-5C133DC8E029}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceFabric.DownstreamService", "samples\ServiceFabric\DownstreamService\Ocelot.Samples.ServiceFabric.DownstreamService.csproj", "{AD09D124-7DD7-4C9E-9BCC-782B579B1786}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {54E84F1A-E525-4443-96EC-039CBD50C263}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {54E84F1A-E525-4443-96EC-039CBD50C263}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}.Release|Any CPU.Build.0 = Release|Any CPU
+ {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {02BBF4C5-517E-4157-8D21-4B8B9E118B7A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F69CEF43-27D2-4940-A47A-FCA879E371BC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EB9F438F-062E-499F-B6EA-4412BEF6D74C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.Build.0 = Release|Any CPU
+ {11C622AD-8C0A-4CF4-811B-3DBB76550797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {11C622AD-8C0A-4CF4-811B-3DBB76550797}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {11C622AD-8C0A-4CF4-811B-3DBB76550797}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {11C622AD-8C0A-4CF4-811B-3DBB76550797}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A7F0CAFA-AECB-43CA-BE89-5F5B728E7C22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7F0CAFA-AECB-43CA-BE89-5F5B728E7C22}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A7F0CAFA-AECB-43CA-BE89-5F5B728E7C22}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A7F0CAFA-AECB-43CA-BE89-5F5B728E7C22}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F00C73F4-019D-490D-8194-CA1754D717FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F00C73F4-019D-490D-8194-CA1754D717FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F00C73F4-019D-490D-8194-CA1754D717FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F00C73F4-019D-490D-8194-CA1754D717FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FECB0C8B-5778-4441-B10E-0C815F5106D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FECB0C8B-5778-4441-B10E-0C815F5106D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FECB0C8B-5778-4441-B10E-0C815F5106D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FECB0C8B-5778-4441-B10E-0C815F5106D5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {28AD7065-8DB1-4711-83BF-9EA47D75F8F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {28AD7065-8DB1-4711-83BF-9EA47D75F8F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {28AD7065-8DB1-4711-83BF-9EA47D75F8F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {28AD7065-8DB1-4711-83BF-9EA47D75F8F7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {869EE931-7E4A-40AA-ADDD-D20DF34C3BB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {869EE931-7E4A-40AA-ADDD-D20DF34C3BB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {869EE931-7E4A-40AA-ADDD-D20DF34C3BB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {869EE931-7E4A-40AA-ADDD-D20DF34C3BB3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {681B6E08-114D-4B9B-8F82-E370CA29B8EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {681B6E08-114D-4B9B-8F82-E370CA29B8EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {681B6E08-114D-4B9B-8F82-E370CA29B8EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {681B6E08-114D-4B9B-8F82-E370CA29B8EC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {161DD558-993D-491B-AD20-966127D71E49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {161DD558-993D-491B-AD20-966127D71E49}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {161DD558-993D-491B-AD20-966127D71E49}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {161DD558-993D-491B-AD20-966127D71E49}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DF9EFF21-58D3-428D-8A33-ACFA24E9B6E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DF9EFF21-58D3-428D-8A33-ACFA24E9B6E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DF9EFF21-58D3-428D-8A33-ACFA24E9B6E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DF9EFF21-58D3-428D-8A33-ACFA24E9B6E8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F25EA682-A763-431B-9D88-012A388D3618}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F25EA682-A763-431B-9D88-012A388D3618}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F25EA682-A763-431B-9D88-012A388D3618}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F25EA682-A763-431B-9D88-012A388D3618}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DCBD0AB5-85DD-4F28-9166-0A23969E19EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DCBD0AB5-85DD-4F28-9166-0A23969E19EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DCBD0AB5-85DD-4F28-9166-0A23969E19EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DCBD0AB5-85DD-4F28-9166-0A23969E19EC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D991C694-01F0-4F04-8135-5C133DC8E029}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D991C694-01F0-4F04-8135-5C133DC8E029}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D991C694-01F0-4F04-8135-5C133DC8E029}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D991C694-01F0-4F04-8135-5C133DC8E029}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AD09D124-7DD7-4C9E-9BCC-782B579B1786}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AD09D124-7DD7-4C9E-9BCC-782B579B1786}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AD09D124-7DD7-4C9E-9BCC-782B579B1786}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AD09D124-7DD7-4C9E-9BCC-782B579B1786}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {54E84F1A-E525-4443-96EC-039CBD50C263} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {F8C224FE-36BE-45F5-9B0E-666D8F4A9B52} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {02BBF4C5-517E-4157-8D21-4B8B9E118B7A} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {D4575572-99CA-4530-8737-C296EDA326F8} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {F69CEF43-27D2-4940-A47A-FCA879E371BC} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {EB9F438F-062E-499F-B6EA-4412BEF6D74C} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {9BBD3586-145C-4FA0-91C5-9ED58287D753} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {6045E23D-669C-4F27-AF8E-8EEE6DB3557F} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {72C8E528-B4F5-45CE-8A06-CD3787364856} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {11C622AD-8C0A-4CF4-811B-3DBB76550797} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {A7F0CAFA-AECB-43CA-BE89-5F5B728E7C22} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {F00C73F4-019D-490D-8194-CA1754D717FA} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {FECB0C8B-5778-4441-B10E-0C815F5106D5} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {28AD7065-8DB1-4711-83BF-9EA47D75F8F7} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {869EE931-7E4A-40AA-ADDD-D20DF34C3BB3} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {681B6E08-114D-4B9B-8F82-E370CA29B8EC} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {161DD558-993D-491B-AD20-966127D71E49} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {DF9EFF21-58D3-428D-8A33-ACFA24E9B6E8} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {F25EA682-A763-431B-9D88-012A388D3618} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {DCBD0AB5-85DD-4F28-9166-0A23969E19EC} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {D991C694-01F0-4F04-8135-5C133DC8E029} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ {AD09D124-7DD7-4C9E-9BCC-782B579B1786} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}
+ EndGlobalSection
+EndGlobal
diff --git a/Ocelot.sln b/Ocelot.sln
index e40f83cfb..f09456c44 100644
--- a/Ocelot.sln
+++ b/Ocelot.sln
@@ -1,7 +1,6 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.8.34309.116
+VisualStudioVersion = 17.9.34728.123
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}"
EndProject
@@ -23,8 +22,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5B401523-36DA-4491-B73A-7590A26E420B}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot", "src\Ocelot\Ocelot.csproj", "{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.UnitTests", "test\Ocelot.UnitTests\Ocelot.UnitTests.csproj", "{54E84F1A-E525-4443-96EC-039CBD50C263}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.AcceptanceTests", "test\Ocelot.AcceptanceTests\Ocelot.AcceptanceTests.csproj", "{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52}"
@@ -35,6 +32,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Benchmarks", "test\O
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.IntegrationTests", "test\Ocelot.IntegrationTests\Ocelot.IntegrationTests.csproj", "{D4575572-99CA-4530-8737-C296EDA326F8}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Testing", "test\Ocelot.Testing\Ocelot.Testing.csproj", "{AE6BCCBD-0687-4C58-B30F-4ABBC6422087}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot", "src\Ocelot\Ocelot.csproj", "{D6DF4206-0DBA-41D8-884D-C3E08290FDBB}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Administration", "src\Ocelot.Administration\Ocelot.Administration.csproj", "{F69CEF43-27D2-4940-A47A-FCA879E371BC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Cache.CacheManager", "src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj", "{EB9F438F-062E-499F-B6EA-4412BEF6D74C}"
@@ -43,68 +44,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Consul", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Eureka", "src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj", "{9BBD3586-145C-4FA0-91C5-9ED58287D753}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Polly", "src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj", "{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Kubernetes", "src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{72C8E528-B4F5-45CE-8A06-CD3787364856}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{8FA0CBA0-0338-48EB-B37F-83CA5022237C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.OcelotBasic.ApiGateway", "samples\OcelotBasic\Ocelot.Samples.OcelotBasic.ApiGateway.csproj", "{ED0B3A09-112B-4BA4-82D6-11569BC7A99B}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdministrationApi", "samples\AdministrationApi\AdministrationApi.csproj", "{B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotGraphQL", "samples\OcelotGraphQL\OcelotGraphQL.csproj", "{F43429C3-EC49-464F-9423-9118A36E8FE3}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eureka", "eureka", "{F1CF6F06-5A34-4A6A-8C19-003A78AB0DCF}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiGateway", "samples\OcelotEureka\ApiGateway\ApiGateway.csproj", "{48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DownstreamService", "samples\OcelotEureka\DownstreamService\DownstreamService.csproj", "{32ADF9B3-CBFA-4607-8A8E-1532D90A7197}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "k8s", "k8s", "{4B706988-4817-43A8-ABE1-32A67998C2C8}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.OcelotKube.ApiGateway", "samples\OcelotKube\ApiGateway\Ocelot.Samples.OcelotKube.ApiGateway.csproj", "{8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.OcelotKube.DownstreamService", "samples\OcelotKube\DownstreamService\Ocelot.Samples.OcelotKube.DownstreamService.csproj", "{7B319B8C-8155-4779-BD93-5ABD05CA2AB6}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "service-fabric", "service-fabric", "{B412628F-C325-47E1-A8D9-873DE04C8AF5}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotApplicationApiGateway", "samples\OcelotServiceFabric\src\OcelotApplicationApiGateway\OcelotApplicationApiGateway.csproj", "{8E6DAE6E-E9B1-433A-80C3-1E2640FBA590}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotApplicationService", "samples\OcelotServiceFabric\src\OcelotApplicationService\OcelotApplicationService.csproj", "{33BE6D88-F188-4E60-83AC-3C4B94D24675}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "administration", "administration", "{1F1F324D-6EA4-4E63-A6A7-C6053F412F1A}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "basic", "basic", "{ED066001-BAF7-4117-9884-DF591A56347D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Polly", "src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj", "{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "graphql", "graphql", "{C15CD120-5F8D-41DE-9B21-00E3EA77D6C1}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.OpenTracing", "src\Ocelot.Tracing.OpenTracing\Ocelot.Tracing.OpenTracing.csproj", "{11C622AD-8C0A-4CF4-811B-3DBB76550797}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "open-tracing", "open-tracing", "{731C6A8A-69ED-445C-A132-C638AA93F9C7}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotOpenTracing", "samples\OcelotOpenTracing\OcelotOpenTracing.csproj", "{C9427E78-4281-4F59-A66E-17C0B66550E5}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "service-discovery", "service-discovery", "{25C30AAA-12DD-4BA5-A53F-9271E54EBAB7}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceDiscovery.ApiGateway", "samples\OcelotServiceDiscovery\ApiGateway\Ocelot.Samples.ServiceDiscovery.ApiGateway.csproj", "{D37209EA-C13E-42AE-B851-A8604F1FCD0E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceDiscovery.DownstreamService", "samples\OcelotServiceDiscovery\DownstreamService\Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj", "{E2AC741A-4120-4D59-B5E4-16382ED45E8D}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ocelot.Testing", "test\Ocelot.Testing\Ocelot.Testing.csproj", "{AE6BCCBD-0687-4C58-B30F-4ABBC6422087}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.Build.0 = Release|Any CPU
{54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54E84F1A-E525-4443-96EC-039CBD50C263}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54E84F1A-E525-4443-96EC-039CBD50C263}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -125,6 +78,14 @@ Global
{D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB}.Release|Any CPU.Build.0 = Release|Any CPU
{F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F69CEF43-27D2-4940-A47A-FCA879E371BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F69CEF43-27D2-4940-A47A-FCA879E371BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -141,6 +102,10 @@ Global
{9BBD3586-145C-4FA0-91C5-9ED58287D753}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9BBD3586-145C-4FA0-91C5-9ED58287D753}.Release|Any CPU.Build.0 = Release|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.Build.0 = Release|Any CPU
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -149,106 +114,30 @@ Global
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.Build.0 = Release|Any CPU
- {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.Build.0 = Release|Any CPU
- {ED0B3A09-112B-4BA4-82D6-11569BC7A99B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {ED0B3A09-112B-4BA4-82D6-11569BC7A99B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {ED0B3A09-112B-4BA4-82D6-11569BC7A99B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {ED0B3A09-112B-4BA4-82D6-11569BC7A99B}.Release|Any CPU.Build.0 = Release|Any CPU
- {B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B180F8AE-2F8F-44F9-9E5D-FE65B84B742E}.Release|Any CPU.Build.0 = Release|Any CPU
- {F43429C3-EC49-464F-9423-9118A36E8FE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F43429C3-EC49-464F-9423-9118A36E8FE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F43429C3-EC49-464F-9423-9118A36E8FE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F43429C3-EC49-464F-9423-9118A36E8FE3}.Release|Any CPU.Build.0 = Release|Any CPU
- {48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29}.Release|Any CPU.Build.0 = Release|Any CPU
- {32ADF9B3-CBFA-4607-8A8E-1532D90A7197}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {32ADF9B3-CBFA-4607-8A8E-1532D90A7197}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {32ADF9B3-CBFA-4607-8A8E-1532D90A7197}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {32ADF9B3-CBFA-4607-8A8E-1532D90A7197}.Release|Any CPU.Build.0 = Release|Any CPU
- {8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8500055B-2C51-4CF1-A6EE-F05BB3E9BF16}.Release|Any CPU.Build.0 = Release|Any CPU
- {7B319B8C-8155-4779-BD93-5ABD05CA2AB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7B319B8C-8155-4779-BD93-5ABD05CA2AB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7B319B8C-8155-4779-BD93-5ABD05CA2AB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7B319B8C-8155-4779-BD93-5ABD05CA2AB6}.Release|Any CPU.Build.0 = Release|Any CPU
- {8E6DAE6E-E9B1-433A-80C3-1E2640FBA590}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8E6DAE6E-E9B1-433A-80C3-1E2640FBA590}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8E6DAE6E-E9B1-433A-80C3-1E2640FBA590}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8E6DAE6E-E9B1-433A-80C3-1E2640FBA590}.Release|Any CPU.Build.0 = Release|Any CPU
- {33BE6D88-F188-4E60-83AC-3C4B94D24675}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {33BE6D88-F188-4E60-83AC-3C4B94D24675}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {33BE6D88-F188-4E60-83AC-3C4B94D24675}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {33BE6D88-F188-4E60-83AC-3C4B94D24675}.Release|Any CPU.Build.0 = Release|Any CPU
{11C622AD-8C0A-4CF4-811B-3DBB76550797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11C622AD-8C0A-4CF4-811B-3DBB76550797}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11C622AD-8C0A-4CF4-811B-3DBB76550797}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11C622AD-8C0A-4CF4-811B-3DBB76550797}.Release|Any CPU.Build.0 = Release|Any CPU
- {C9427E78-4281-4F59-A66E-17C0B66550E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C9427E78-4281-4F59-A66E-17C0B66550E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C9427E78-4281-4F59-A66E-17C0B66550E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C9427E78-4281-4F59-A66E-17C0B66550E5}.Release|Any CPU.Build.0 = Release|Any CPU
- {D37209EA-C13E-42AE-B851-A8604F1FCD0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D37209EA-C13E-42AE-B851-A8604F1FCD0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D37209EA-C13E-42AE-B851-A8604F1FCD0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D37209EA-C13E-42AE-B851-A8604F1FCD0E}.Release|Any CPU.Build.0 = Release|Any CPU
- {E2AC741A-4120-4D59-B5E4-16382ED45E8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E2AC741A-4120-4D59-B5E4-16382ED45E8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E2AC741A-4120-4D59-B5E4-16382ED45E8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E2AC741A-4120-4D59-B5E4-16382ED45E8D}.Release|Any CPU.Build.0 = Release|Any CPU
- {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AE6BCCBD-0687-4C58-B30F-4ABBC6422087}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {D6DF4206-0DBA-41D8-884D-C3E08290FDBB} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{54E84F1A-E525-4443-96EC-039CBD50C263} = {5B401523-36DA-4491-B73A-7590A26E420B}
{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52} = {5B401523-36DA-4491-B73A-7590A26E420B}
{02BBF4C5-517E-4157-8D21-4B8B9E118B7A} = {5B401523-36DA-4491-B73A-7590A26E420B}
{106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B}
{D4575572-99CA-4530-8737-C296EDA326F8} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {AE6BCCBD-0687-4C58-B30F-4ABBC6422087} = {5B401523-36DA-4491-B73A-7590A26E420B}
+ {D6DF4206-0DBA-41D8-884D-C3E08290FDBB} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{F69CEF43-27D2-4940-A47A-FCA879E371BC} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{EB9F438F-062E-499F-B6EA-4412BEF6D74C} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{9BBD3586-145C-4FA0-91C5-9ED58287D753} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
+ {72C8E528-B4F5-45CE-8A06-CD3787364856} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
- {72C8E528-B4F5-45CE-8A06-CD3787364856} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
- {ED0B3A09-112B-4BA4-82D6-11569BC7A99B} = {ED066001-BAF7-4117-9884-DF591A56347D}
- {B180F8AE-2F8F-44F9-9E5D-FE65B84B742E} = {1F1F324D-6EA4-4E63-A6A7-C6053F412F1A}
- {F43429C3-EC49-464F-9423-9118A36E8FE3} = {C15CD120-5F8D-41DE-9B21-00E3EA77D6C1}
- {F1CF6F06-5A34-4A6A-8C19-003A78AB0DCF} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
- {48B3DD3C-7F4D-40C1-A104-3BF9EF4ACE29} = {F1CF6F06-5A34-4A6A-8C19-003A78AB0DCF}
- {32ADF9B3-CBFA-4607-8A8E-1532D90A7197} = {F1CF6F06-5A34-4A6A-8C19-003A78AB0DCF}
- {4B706988-4817-43A8-ABE1-32A67998C2C8} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
- {8500055B-2C51-4CF1-A6EE-F05BB3E9BF16} = {4B706988-4817-43A8-ABE1-32A67998C2C8}
- {7B319B8C-8155-4779-BD93-5ABD05CA2AB6} = {4B706988-4817-43A8-ABE1-32A67998C2C8}
- {B412628F-C325-47E1-A8D9-873DE04C8AF5} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
- {8E6DAE6E-E9B1-433A-80C3-1E2640FBA590} = {B412628F-C325-47E1-A8D9-873DE04C8AF5}
- {33BE6D88-F188-4E60-83AC-3C4B94D24675} = {B412628F-C325-47E1-A8D9-873DE04C8AF5}
- {1F1F324D-6EA4-4E63-A6A7-C6053F412F1A} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
- {ED066001-BAF7-4117-9884-DF591A56347D} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
- {C15CD120-5F8D-41DE-9B21-00E3EA77D6C1} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
{11C622AD-8C0A-4CF4-811B-3DBB76550797} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
- {731C6A8A-69ED-445C-A132-C638AA93F9C7} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
- {C9427E78-4281-4F59-A66E-17C0B66550E5} = {731C6A8A-69ED-445C-A132-C638AA93F9C7}
- {25C30AAA-12DD-4BA5-A53F-9271E54EBAB7} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
- {D37209EA-C13E-42AE-B851-A8604F1FCD0E} = {25C30AAA-12DD-4BA5-A53F-9271E54EBAB7}
- {E2AC741A-4120-4D59-B5E4-16382ED45E8D} = {25C30AAA-12DD-4BA5-A53F-9271E54EBAB7}
- {AE6BCCBD-0687-4C58-B30F-4ABBC6422087} = {5B401523-36DA-4491-B73A-7590A26E420B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}
diff --git a/ReleaseNotes.md b/ReleaseNotes.md
index 48db3c3bf..3a0da1bdb 100644
--- a/ReleaseNotes.md
+++ b/ReleaseNotes.md
@@ -1,31 +1,84 @@
-## Hotfix release (version {0}) for #2031 issue
-> Route path template placeholders and their validation rules
+## Spring 2024 (version {0}) aka [Twilight Texas](https://www.timeanddate.com/eclipse/solar/2024-april-8) release
+> Codenamed: **[Twilight Texas](https://www.timeanddate.com/eclipse/solar/2024-april-8)**
+> Read the Docs: [Ocelot 23.3](https://ocelot.readthedocs.io/en/{0}/)
-Special thanks to **[Guillaume Gnaegi](https://github.com/ggnaegi)** and [Fabrizio Mancin](https://github.com/Fabman08)!
+### What's new?
-### About
-The bug is related to the [Placeholders](https://ocelot.readthedocs.io/en/latest/features/routing.html#placeholders) feature in [Configuration](https://ocelot.readthedocs.io/en/latest/features/configuration.html) and [Routing](https://ocelot.readthedocs.io/en/latest/features/routing.html).
-The bug was introduced in version [23.2.0](https://github.com/ThreeMammals/Ocelot/releases/tag/23.2.0) as a part of PR #1927.
+- **[Service Discovery](https://github.com/ThreeMammals/Ocelot/blob/main/docs/features/servicediscovery.rst)**: Introducing a new feature for "[Customization of services creation](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/servicediscovery.rst#consul-service-builder-3)" in two primary service discovery providers: [Consul](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/servicediscovery.rst#consul-service-builder-3) and [Kubernetes](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/kubernetes.rst#downstream-scheme-vs-port-names-3), developed by @raman-m.
+ The customization for both `Consul` and `Kube` providers in service creation is achieved through the overriding of virtual methods in default implementations. The recommendation was to separate the provider's logic and introduce `public virtual` and `protected virtual` methods in concrete classes, enabling:
+ - The use of `public virtual` methods as dictated by interface definitions.
+ - The application of `protected virtual` methods to allow developers to customize atomic operations through inheritance from existing concrete classes.
+ - The injection of new interface objects into the provider's constructor.
+ - The overriding of the default behavior of classes.
-### Breaking Change
-The new [validation rules](https://github.com/ThreeMammals/Ocelot/blob/23.2.0/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs#L45-L50) of the `FileConfigurationFluentValidator` class do not allow the Ocelot app to start when implicit [placeholders](https://ocelot.readthedocs.io/en/latest/features/routing.html#placeholders) are defined in custom implementations, such as middlewares, delegating handlers, and replaced services in the dependency injection (DI) container.
-These new rules are capable of validating explicit [placeholders](https://ocelot.readthedocs.io/en/latest/features/routing.html#placeholders) only within the `UpstreamPathTemplate` and `DownstreamPathTemplate` properties. Unfortunately, they cannot oversee implicit placeholders in custom implementations, and they do not validate early during the Ocelot app startup process.
+ Ultimately, customization relies on the virtual methods within the default implementation classes, providing developers the flexibility to override them as necessary for highly tailored Consul/K8s configurations in their specific environments.
+ For further details, refer to the respective pull requests for both providers:
+ - `Kube` → PR #2052
+ - `Consul` → PR #2067
-Ensure that you avoid using version [23.2.0](https://github.com/ThreeMammals/Ocelot/releases/tag/23.2.0). If you are currently on that version, upgrade to version [{0}](https://github.com/ThreeMammals/Ocelot/releases/tag/{0}) by applying this hotfix patch.
+- **[Routing](https://github.com/ThreeMammals/Ocelot/blob/main/docs/features/routing.rst)**: Introducing the new "[Routing based on Request Header](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/routing.rst#upstream-headers-3)" feature by @jlukawska.
+ In addition to routing via `UpstreamPathTemplate`, you can now define an `UpstreamHeaderTemplates` options dictionary. For a route to match, all headers specified in this section are required to be present in the request headers.
+ For more details, see PR #1312.
-### Technical info
-With version [23.2.0](https://github.com/ThreeMammals/Ocelot/releases/tag/23.2.0), particularly if you have overridden certain service classes or implemented custom logic that manipulates placeholders, you may encounter Ocelot app crashes accompanied by the following errors in the log:
-```
-One or more errors occurred. (Unable to start Ocelot, errors are: XXX)
-```
-where `XXX` are the following validation error messages:
-- `UpstreamPathTemplate 'UUU' doesn't contain the same placeholders in DownstreamPathTemplate 'DDD'`
-- `DownstreamPathTemplate 'DDD' doesn't contain the same placeholders in UpstreamPathTemplate 'UUU'`
+- **[Configuration](https://github.com/ThreeMammals/Ocelot/blob/main/docs/features/configuration.rst)**: Introducing the "[Custom Default Version Policy](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/configuration.rst#downstreamhttpversionpolicy-3)" feature by @ibnuda.
+ The configurable `HttpRequestMessage.VersionPolicy` helps avoid HTTP protocol connection errors and stabilizes connections to downstream services, especially when you're not developing those services, documentation is scarce, or the deployed HTTP protocol version is uncertain.
+ For developers of downstream services, it's possible to `ConfigureKestrel` server and its endpoints with new protocol settings. However, attention to version policy is also required, and this feature provides precise version settings for HTTP connections.
+ Essentially, this feature promotes the use of HTTP protocols beyond 1.0/1.1, such as HTTP/2 or even HTTP/3.
+ For additional details, refer to PR #1673.
-**Finally**, the [validation rules](https://github.com/ThreeMammals/Ocelot/blob/23.2.0/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs#L45-L50) resulted from the incorrect assumption that placeholders are always explicit and can be validated early. Therefore, custom implementations and feature services in the dependency injection (DI) container, which rely on or manipulate placeholders, should validate the configuration JSON and appropriate options later, directly within their service implementations.
+- **[Configuration](https://github.com/ThreeMammals/Ocelot/blob/main/docs/features/configuration.rst)**: Introducing the new "[Route Metadata](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/configuration.rst#route-metadata)" feature by @vantm. Undoubtedly, this is the standout feature of the release! :star:
+ Route metadata enables Ocelot developers to incorporate custom functions that address specific needs or to create their own plugins/extensions.
+ In versions of Ocelot prior to [{0}](https://github.com/ThreeMammals/Ocelot/releases/tag/{0}), the configuration was limited to predefined values that Ocelot used internally. This was sufficient for official extensions, but posed challenges for third-party developers who needed to implement configurations not included in the standard `FileConfiguration`. Applying an option to a specific route required knowledge of the array index and other details that might not be readily accessible using the standard `IConfiguration` or `IOptions` models from ASP.NET. Now, metadata can be directly accessed in the `DownstreamRoute` object. Furthermore, metadata can also be retrieved from the global JSON section via the `FileConfiguration.GlobalConfiguration` property.
+ For more information, see the details in PR #1843 on this remarkable feature.
-### Bug Artifacts
-- Released in version: [23.2.0](https://github.com/ThreeMammals/Ocelot/releases/tag/23.2.0)
-- Introduced in: PR #1927
-- Reported bug: #2031 by @ggnaegi and tested by @Fabman08
-- Hotfix PR: #2032 by @raman-m
+### Focus On
+
+
+ Updates of the features: Configuration, Service Discovery, Routing and Quality of Service
+
+ - [Configuration](https://github.com/ThreeMammals/Ocelot/blob/main/docs/features/configuration.rst): New features are "[Custom Default Version Policy](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/configuration.rst#downstreamhttpversionpolicy-3)" by @ibnuda and "[Route Metadata](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/configuration.rst#route-metadata)" by @vantm.
+
+ - [Service Discovery](https://github.com/ThreeMammals/Ocelot/blob/main/docs/features/servicediscovery.rst): New feature is "[Customization of services creation](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/servicediscovery.rst#consul-service-builder-3)" by @raman-m.
+
+ - [Routing](https://github.com/ThreeMammals/Ocelot/blob/main/docs/features/routing.rst): New feature is "[Routing based on Request Header](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/routing.rst#upstream-headers-3)" by @jlukawska.
+
+ - [Quality of Service](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/qualityofservice.rst): The team has decided to remove the Polly V7 policies logic and the corresponding Ocelot `AddPollyV7` extensions (referenced in PR #2079). Furthermore, the Polly V8 Circuit Breaker has been mandated as the primary strategy (as per PR #2086).
+ See more detaild below in "**Ocelot extra packages**" paragraph.
+
+
+
+ Ocelot extra packages
+
+ - **[Ocelot.Provider.Polly](https://www.nuget.org/packages/Ocelot.Provider.Polly)**
+
+ - Our team has resolved to eliminate the Polly V7 policies logic and the corresponding Ocelot `AddPollyV7` extensions entirely (refer to the "[Polly v7 vs v8](https://github.com/ThreeMammals/Ocelot/blob/23.2.0/docs/features/qualityofservice.rst#polly-v7-vs-v8)" documentation). In the previous [23.2](https://github.com/ThreeMammals/Ocelot/releases/tag/23.2.0) release, named [Lunar Eclipse](https://github.com/ThreeMammals/Ocelot/releases/tag/23.2.0), we included these to maintain the legacy Polly behavior, allowing development teams to transition or retain the old Polly V7 functionality. We are now confident that it is time to progress alongside Polly, shifting our focus to the new Polly V8 [resilience pipelines](https://www.pollydocs.org/pipelines/). For more details, see PR #2079.
+
+ - Additionally, we have implemented Polly v8 Circuit Breaker as the primary strategy. Our Quality of Service (QoS) relies on two main strategies: [Circuit Breaker](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/qualityofservice.rst#circuit-breaker-strategy) and [Timeout](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/qualityofservice.rst#timeout-strategy). If both Circuit Breaker and Timeout are [configured](https://github.com/ThreeMammals/Ocelot/blob/{0}/docs/features/qualityofservice.rst#configuration) with their respective properties in the `QoSOptions` of the route JSON, then the Circuit Breaker strategy will take precedence in the constructed resilience pipeline. For more details, refer to PR #2086.
+
+
+
+ Stabilization aka bug fixing
+
+ - Fixed #2034 in PR #2045 by @raman-m
+ - Fixed #2039 in PR #2050 by @PaulARoy
+ - Fixed #1590 in PR #1592 by @sergio-str
+ - Fixed #2054 #2059 in PR #2058 by @thiagoloureiro
+ - Fixed #954 #957 #1026 in PR #2067 by @raman-m
+ - Fixed #2002 in PR #2003 by @bbenameur
+ - Fixed #2085 in PR #2086 by @RaynaldM
+ - See [all bugs](https://github.com/ThreeMammals/Ocelot/issues?q=is%3Aissue+milestone%3ASpring%2724+is%3Aclosed+label%3Abug) of the [Spring'24](https://github.com/ThreeMammals/Ocelot/milestone/6) milestone
+
+
+
+ Documentation for version {0}
+
+ - [Caching](https://ocelot.readthedocs.io/en/{0}/features/caching.html): New [EnableContentHashing option](https://ocelot.readthedocs.io/en/{0}/features/caching.html#enablecontenthashing-option) and [Global Configuration](https://ocelot.readthedocs.io/en/{0}/features/caching.html#global-configuration) sections
+ - [Configuration](https://ocelot.readthedocs.io/en/{0}/features/configuration.html): New [DownstreamHttpVersionPolicy](https://ocelot.readthedocs.io/en/{0}/features/configuration.html#downstreamhttpversionpolicy-3) and [Route Metadata](https://ocelot.readthedocs.io/en/{0}/features/configuration.html#route-metadata)
+ - [Kubernetes](https://ocelot.readthedocs.io/en/{0}/features/kubernetes.html): New [Downstream Scheme vs Port Names](https://ocelot.readthedocs.io/en/{0}/features/kubernetes.html#downstream-scheme-vs-port-names-3) section
+ - [Metadata](https://ocelot.readthedocs.io/en/{0}/features/metadata.html): This is new chapter for [Route Metadata](https://ocelot.readthedocs.io/en/{0}/features/configuration.html#route-metadata) feature.
+ - [Quality of Service](https://ocelot.readthedocs.io/en/{0}/features/qualityofservice.html)
+ - [Rate Limiting](https://ocelot.readthedocs.io/en/{0}/features/ratelimiting.html)
+ - [Request Aggregation](https://ocelot.readthedocs.io/en/{0}/features/requestaggregation.html)
+ - [Routing](https://ocelot.readthedocs.io/en/{0}/features/routing.html): New [Upstream Headers](https://ocelot.readthedocs.io/en/{0}/features/routing.html#upstream-headers-3) section
+ - [Service Discovery](https://ocelot.readthedocs.io/en/{0}/features/servicediscovery.html): New [Consul Service Builder](https://ocelot.readthedocs.io/en/{0}/features/servicediscovery.html#consul-service-builder-3) section
+
diff --git a/build.cake b/build.cake
index f7896932f..430a1c804 100644
--- a/build.cake
+++ b/build.cake
@@ -1,9 +1,9 @@
-#tool "dotnet:?package=GitVersion.Tool&version=5.8.1"
-#tool "dotnet:?package=coveralls.net&version=4.0.1"
-#addin nuget:?package=Newtonsoft.Json
-#addin nuget:?package=System.Text.Encodings.Web&version=4.7.1
-#tool "nuget:?package=ReportGenerator&version=5.2.0"
-#addin Cake.Coveralls&version=1.1.0
+#tool dotnet:?package=GitVersion.Tool&version=5.12.0 // 6.0.0-beta.7 supports .NET 8, 7, 6
+#tool dotnet:?package=coveralls.net&version=4.0.1
+#tool nuget:?package=ReportGenerator&version=5.2.4
+#addin nuget:?package=Newtonsoft.Json&version=13.0.3
+#addin nuget:?package=System.Text.Encodings.Web&version=8.0.0
+#addin nuget:?package=Cake.Coveralls&version=1.1.0
#r "Spectre.Console"
using Spectre.Console
@@ -13,10 +13,8 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
-// compile
-var compileConfig = Argument("configuration", "Release");
-
-var slnFile = "./Ocelot.sln";
+const string Release = "Release"; // task name, target, and Release config name
+var compileConfig = Argument("configuration", Release); // compile
// build artifacts
var artifactsDir = Directory("artifacts");
@@ -61,9 +59,10 @@ string gitHubUsername = "TomPallister";
string gitHubPassword = Environment.GetEnvironmentVariable("OCELOT_GITHUB_API_KEY");
var target = Argument("target", "Default");
-
-Information("target is " + target);
-Information("Build configuration is " + compileConfig);
+var slnFile = (target == Release) ? $"./Ocelot.{Release}.sln" : "./Ocelot.sln";
+Information("\nTarget: " + target);
+Information("Build: " + compileConfig);
+Information("Solution: " + slnFile);
TaskTeardown(context => {
AnsiConsole.Markup($"[green]DONE[/] {context.Task.Name}\n");
@@ -83,7 +82,7 @@ Task("RunTests")
.IsDependentOn("RunAcceptanceTests")
.IsDependentOn("RunIntegrationTests");
-Task("Release")
+Task(Release)
.IsDependentOn("Build")
.IsDependentOn("CreateReleaseNotes")
.IsDependentOn("CreateArtifacts")
@@ -95,11 +94,18 @@ Task("Compile")
.IsDependentOn("Version")
.Does(() =>
{
+ Information("Build: " + compileConfig);
+ Information("Solution: " + slnFile);
var settings = new DotNetBuildSettings
{
Configuration = compileConfig,
};
-
+ if (target != Release)
+ {
+ settings.Framework = "net8.0"; // build using .NET 8 SDK only
+ }
+ Information($"Settings {nameof(DotNetBuildSettings.Framework)}: {settings.Framework}");
+ Information($"Settings {nameof(DotNetBuildSettings.Configuration)}: {settings.Configuration}");
DotNetBuild(slnFile, settings);
});
@@ -168,9 +174,10 @@ Task("CreateReleaseNotes")
return;
}
- var shortlogSummary = GitHelper($"shortlog --no-merges --numbered --summary {lastRelease}..HEAD")
+ var debugUserEmail = false;
+ var shortlogSummary = GitHelper($"shortlog --no-merges --numbered --summary --email {lastRelease}..HEAD")
.ToList();
- var re = new Regex(@"^[\s\t]*(?'commits'\d+)[\s\t]+(?'author'.*)$");
+ var re = new Regex(@"^[\s\t]*(?'commits'\d+)[\s\t]+(?'author'.*)[\s\t]+<(?'email'.*)>.*$");
var summary = shortlogSummary
.Where(x => re.IsMatch(x))
.Select(x => re.Match(x))
@@ -178,6 +185,7 @@ Task("CreateReleaseNotes")
{
commits = int.Parse(m.Groups["commits"]?.Value ?? "0"),
author = m.Groups["author"]?.Value?.Trim() ?? string.Empty,
+ email = m.Groups["email"]?.Value?.Trim() ?? string.Empty,
})
.ToList();
@@ -186,13 +194,18 @@ Task("CreateReleaseNotes")
foreach (var contributor in summary)
{
var stars = string.Join(string.Empty, Enumerable.Repeat(":star:", contributor.commits));
- starring.Add($"{stars} {contributor.author}");
+ var emailInfo = debugUserEmail ? ", " + contributor.email : string.Empty;
+ starring.Add($"{stars} {contributor.author}{emailInfo}");
}
// Honoring aka Top Contributors
const int top3 = 3; // going to create Top 3
var topContributors = new List();
+ // Ocelot Core team members should not be in Top 3 Chart
+ var coreTeamNames = new List { "Raman Maksimchuk", "Raynald Messié", "Guillaume Gnaegi" };
+ var coreTeamEmails = new List { "dotnet044@gmail.com", "redbird_project@yahoo.fr", "58469901+ggnaegi@users.noreply.github.com" };
var commitsGrouping = summary
+ .Where(x => !coreTeamNames.Contains(x.author) && !coreTeamEmails.Contains(x.email)) // filter out Ocelot Core team members
.GroupBy(x => x.commits)
.Select(g => new
{
@@ -204,7 +217,7 @@ Task("CreateReleaseNotes")
.ToList();
// local helpers
- string[] places = new[] { "1st", "2nd", "3rd" };
+ string[] places = new[] { "1st", "2nd", "3rd", "4", "5", "6", "7", "8", "9", "10", "11" };
static string Plural(int n) => n == 1 ? "" : "s";
static string Honor(string place, string author, int commits, string suffix = null)
=> $"{place[0]}{place[1..]} :{place}_place_medal: goes to **{author}** for delivering **{commits}** feature{Plural(commits)} {suffix ?? ""}";
@@ -306,11 +319,11 @@ Task("CreateReleaseNotes")
}
} // END of Top 3
- // releaseNotes.Add("### Honoring :medal_sports: aka Top Contributors :clap:");
- // releaseNotes.AddRange(topContributors);
- // releaseNotes.Add("");
- // releaseNotes.Add("### Starring :star: aka Release Influencers :bowtie:");
- // releaseNotes.AddRange(starring);
+ releaseNotes.Add("### Honoring :medal_sports: aka Top Contributors :clap:");
+ releaseNotes.AddRange(topContributors);
+ releaseNotes.Add("");
+ releaseNotes.Add("### Starring :star: aka Release Influencers :bowtie:");
+ releaseNotes.AddRange(starring);
releaseNotes.Add("");
releaseNotes.Add($"### Features in Release {releaseVersion}");
var commitsHistory = GitHelper($"log --no-merges --date=format:\"%A, %B %d at %H:%M\" --pretty=format:\"%h by **%aN** on %ad →%n%s\" {lastRelease}..HEAD");
@@ -344,15 +357,23 @@ Task("RunUnitTests")
{
Configuration = compileConfig,
ResultsDirectory = artifactsForUnitTestsDir,
- ArgumentCustomization = args => args
- // this create the code coverage report
- .Append("--collect:\"XPlat Code Coverage\"")
+ ArgumentCustomization = args => args
+ .Append("--no-restore")
+ .Append("--no-build")
+ .Append("--collect:\"XPlat Code Coverage\"") // this create the code coverage report
+ .Append("--verbosity:detailed")
+ .Append("--consoleLoggerParameters:ErrorsOnly")
};
-
+ if (target != Release)
+ {
+ testSettings.Framework = "net8.0"; // .NET 8 SDK only
+ }
EnsureDirectoryExists(artifactsForUnitTestsDir);
DotNetTest(unitTestAssemblies, testSettings);
- var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.cobertura.xml"));
+ var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir)
+ .First()
+ .CombineWithFilePath(File("coverage.cobertura.xml"));
Information(coverageSummaryFile);
Information(artifactsForUnitTestsDir);
@@ -396,11 +417,15 @@ Task("RunAcceptanceTests")
var settings = new DotNetTestSettings
{
Configuration = compileConfig,
+ Framework = "net8.0", // .NET 8 SDK only
ArgumentCustomization = args => args
.Append("--no-restore")
.Append("--no-build")
};
-
+ if (target != Release)
+ {
+ settings.Framework = "net8.0"; // .NET 8 SDK only
+ }
EnsureDirectoryExists(artifactsForAcceptanceTestsDir);
DotNetTest(acceptanceTestAssemblies, settings);
});
@@ -412,11 +437,15 @@ Task("RunIntegrationTests")
var settings = new DotNetTestSettings
{
Configuration = compileConfig,
+ Framework = "net8.0", // .NET 8 SDK only
ArgumentCustomization = args => args
.Append("--no-restore")
.Append("--no-build")
};
-
+ if (target != Release)
+ {
+ settings.Framework = "net8.0"; // .NET 8 SDK only
+ }
EnsureDirectoryExists(artifactsForIntegrationTestsDir);
DotNetTest(integrationTestAssemblies, settings);
});
diff --git a/docs/_static/overrides.css b/docs/_static/overrides.css
new file mode 100644
index 000000000..a5f45454c
--- /dev/null
+++ b/docs/_static/overrides.css
@@ -0,0 +1,6 @@
+blockquote {
+ font-size: 0.9em;
+}
+aside.footnote-list {
+ font-size: 0.9em;
+}
diff --git a/docs/conf.py b/docs/conf.py
index 41b0fd36d..2c31a6b98 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -9,7 +9,7 @@
project = 'Ocelot'
copyright = ' 2016-2024 ThreeMammals Ocelot team'
author = 'Tom Pallister, Raman Maksimchuk and Ocelot Core team at ThreeMammals'
-release = '23.2'
+release = '23.3'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -27,3 +27,4 @@
# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_static_path
html_static_path = ['_static']
+html_css_files = ['overrides.css']
diff --git a/docs/features/caching.rst b/docs/features/caching.rst
index f2bf302ed..3d14b47c9 100644
--- a/docs/features/caching.rst
+++ b/docs/features/caching.rst
@@ -39,7 +39,8 @@ Finally, in order to use caching on a route in your Route configuration add this
"FileCacheOptions": {
"TtlSeconds": 15,
"Region": "europe-central",
- "Header": "Authorization"
+ "Header": "OC-Caching-Control",
+ "EnableContentHashing": false // my route has GET verb only, assigning 'true' for requests with body: POST, PUT etc.
}
In this example **TtlSeconds** is set to 15 which means the cache will expire after 15 seconds.
@@ -48,10 +49,40 @@ The **Region** represents a region of caching.
Additionally, if a header name is defined in the **Header** property, that header value is looked up by the key (header name) in the ``HttpRequest`` headers,
and if the header is found, its value will be included in caching key. This causes the cache to become invalid due to the header value changing.
+.. _cch-enablecontenthashing-option:
+
+``EnableContentHashing`` option
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In version `23.0`_, the new property **EnableContentHashing** has been introduced. Previously, the request body was utilized to compute the cache key.
+However, due to potential performance issues arising from request body hashing, it has been disabled by default.
+Clearly, this constitutes a breaking change and presents challenges for users who require cache key calculations that consider the request body (e.g., for the POST method).
+To address this issue, it is recommended to enable the option either at the route level or globally in the :ref:`cch-global-configuration` section:
+
+.. code-block:: json
+
+ "CacheOptions": {
+ // ...
+ "EnableContentHashing": true
+ }
+
+.. _cch-global-configuration:
+
+Global Configuration
+--------------------
+
+The positive update is that copying Route-level properties for each route is no longer necessary, as version `23.3`_ allows for setting their values in the ``GlobalConfiguration``.
+This convenience extends to **Header** and **Region** as well.
+However, an alternative is still being sought for **TtlSeconds**, which must be explicitly set for each route to enable caching.
+
+Notes
+-----
+
If you look at the example `here `_ you can see how the cache manager is setup and then passed into the Ocelot ``AddCacheManager`` configuration method.
You can use any settings supported by the **CacheManager** package and just pass them in.
-Anyway, Ocelot currently supports caching on the URL of the downstream service and setting a TTL in seconds to expire the cache. You can also clear the cache for a region by calling Ocelot's administration API.
+Anyway, Ocelot currently supports caching on the URL of the downstream service and setting a TTL in seconds to expire the cache.
+You can also clear the cache for a region by calling Ocelot's administration API.
Your Own Caching
----------------
@@ -68,3 +99,6 @@ If you want to add your own caching method, implement the following interfaces a
Please dig into the Ocelot source code to find more.
We would really appreciate it if anyone wants to implement `Redis `_, `Memcached `_ etc.
Please, open a new `Show and tell `_ thread in `Discussions `_ space of the repository.
+
+.. _23.0: https://github.com/ThreeMammals/Ocelot/releases/tag/23.0.0
+.. _23.3: https://github.com/ThreeMammals/Ocelot/releases/tag/23.3.0
diff --git a/docs/features/configuration.rst b/docs/features/configuration.rst
index 552767f49..1be532e08 100644
--- a/docs/features/configuration.rst
+++ b/docs/features/configuration.rst
@@ -19,11 +19,14 @@ Here is an example Route configuration. You don't need to set all of these thing
.. code-block:: json
{
- "DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/",
+ "UpstreamHeaderTemplates": {}, // dictionary
+ "UpstreamHost": "",
"UpstreamHttpMethod": [ "Get" ],
+ "DownstreamPathTemplate": "/",
"DownstreamHttpMethod": "",
"DownstreamHttpVersion": "",
+ "DownstreamHttpVersionPolicy": "",
"AddHeadersToRequest": {},
"AddClaimsToRequest": {},
"RouteClaimsRequirement": {},
@@ -37,7 +40,7 @@ Here is an example Route configuration. You don't need to set all of these thing
"ServiceName": "",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
- { "Host": "localhost", "Port": 51876 }
+ { "Host": "localhost", "Port": 12345 }
],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 0,
@@ -67,10 +70,12 @@ Here is an example Route configuration. You don't need to set all of these thing
"IPAllowedList": [],
"IPBlockedList": [],
"ExcludeAllowedFromBlocked": false
- }
+ },
+ "Metadata": {}
}
-More information on how to use these options is below.
+The actual Route schema for properties can be found in the C# `FileRoute `_ class.
+If you're interested in learning more about how to utilize these options, read below!
Multiple Environments
---------------------
@@ -123,9 +128,16 @@ If you want to set the **GlobalConfiguration** property, you must have a file ca
The way Ocelot merges the files is basically load them, loop over them, add any **Routes**, add any **AggregateRoutes** and if the file is called ``ocelot.global.json`` add the **GlobalConfiguration** aswell as any **Routes** or **AggregateRoutes**.
Ocelot will then save the merged configuration to a file called `ocelot.json`_ and this will be used as the source of truth while Ocelot is running.
-At the moment there is no validation at this stage it only happens when Ocelot validates the final merged configuration.
-This is something to be aware of when you are investigating problems.
-We would advise always checking what is in `ocelot.json`_ file if you have any problems.
+ **Note 1**: Currently, validation occurs only during the final merging of configurations in Ocelot.
+ It's essential to be aware of this when troubleshooting issues.
+ We recommend thoroughly inspecting the contents of the ``ocelot.json`` file if you encounter any problems.
+
+ **Note 2**: The Merging feature is operational only during the application's startup.
+ Consequently, the merged configuration in ``ocelot.json`` remains static post-merging and startup.
+ It's important to be aware that the ``ConfigureAppConfiguration`` method is invoked solely during the startup of an ASP.NET web application.
+ Once the Ocelot application has started, you cannot call the ``AddOcelot`` method, nor can you employ the merging feature within ``AddOcelot``.
+ If you still require on-the-fly updating of the primary configuration file, ``ocelot.json``, please refer to the :ref:`config-react-to-changes` section.
+ Additionally, note that merging partial configuration files (such as ``ocelot.*.json``) on the fly using :doc:`../features/administration` API is not currently implemented.
Keep files in a folder
^^^^^^^^^^^^^^^^^^^^^^
@@ -204,62 +216,14 @@ For example:
Examining the code within the `ConfigurationBuilderExtensions class `_ would be helpful for gaining a better understanding of the signatures of the overloaded methods [#f2]_.
-Store Configuration in Consul
------------------------------
-
-The first thing you need to do is install the `NuGet package `_ that provides `Consul `_ support in Ocelot.
-
-.. code-block:: powershell
-
- Install-Package Ocelot.Provider.Consul
-
-Then you add the following when you register your services Ocelot will attempt to store and retrieve its configuration in Consul KV store.
-In order to register Consul services we must call the ``AddConsul()`` and ``AddConfigStoredInConsul()`` extensions using the ``OcelotBuilder`` being returned by ``AddOcelot()`` [#f3]_ like below:
-
-.. code-block:: csharp
+Store Configuration in `Consul`_
+--------------------------------
- services.AddOcelot()
- .AddConsul()
- .AddConfigStoredInConsul();
+As a developer, if you have enabled :doc:`../features/servicediscovery` with `Consul`_ support in Ocelot, you may choose to manage your configuration saving to the *Consul* `KV store`_.
-You also need to add the following to your `ocelot.json`_. This is how Ocelot finds your Consul agent and interacts to load and store the configuration from Consul.
-
-.. code-block:: json
-
- "GlobalConfiguration": {
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 9500
- }
- }
-
-The team decided to create this feature after working on the Raft consensus algorithm and finding out its super hard.
-Why not take advantage of the fact Consul already gives you this!
-We guess it means if you want to use Ocelot to its fullest, you take on Consul as a dependency for now.
-
-This feature has a `3 seconds `_ TTL cache before making a new request to your local Consul agent.
-
-.. _config-consul-key:
-
-Consul Configuration Key [#f4]_
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Beyond the traditional methods of storing configuration in a file vs folder (:ref:`config-merging-files`), or in-memory (:ref:`config-merging-tomemory`), you also have the alternative to utilize the `Consul`_ server's storage capabilities.
-If you are using Consul for configuration (or other providers in the future), you might want to key your configurations: so you can have multiple configurations.
-
-In order to specify the key you need to set the **ConfigurationKey** property in the **ServiceDiscoveryProvider** options of the configuration JSON file e.g.
-
-.. code-block:: json
-
- "GlobalConfiguration": {
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 9500,
- "ConfigurationKey": "Ocelot_A"
- }
- }
-
-In this example Ocelot will use ``Ocelot_A`` as the key for your configuration when looking it up in Consul.
-If you do not set the **ConfigurationKey**, Ocelot will use the string ``InternalConfiguration`` as the key.
+For further details on managing Ocelot configurations via a Consul instance, please consult the ":ref:`sd-consul-configuration-in-kv`" section.
Follow Redirects aka HttpHandlerOptions
---------------------------------------
@@ -336,6 +300,8 @@ As a team, we highly recommend following these instructions when developing your
System administrators or DevOps engineers must create real valid certificates being signed by hosting or cloud providers.
**Switch off the feature for all routes!** Remove the **DangerousAcceptAnyServerCertificateValidator** property for all routes in production version of `ocelot.json`_ file!
+.. _config-react-to-changes:
+
React to Configuration Changes
------------------------------
@@ -392,11 +358,76 @@ Registering a callback
}
}
+.. _config-http-version:
+
DownstreamHttpVersion
---------------------
Ocelot allows you to choose the HTTP version it will use to make the proxy request. It can be set as ``1.0``, ``1.1`` or ``2.0``.
+* `HttpVersion Class `_
+
+.. _config-version-policy:
+
+DownstreamHttpVersionPolicy [#f3]_
+----------------------------------
+
+This routing property enables the configuration of the ``VersionPolicy`` property within ``HttpRequestMessage`` objects for downstream HTTP requests.
+For additional details, refer to the following documentation:
+
+* `HttpRequestMessage.VersionPolicy Property `_
+* `HttpVersionPolicy Enum `_
+* `HttpVersion Class `_
+
+The ``DownstreamHttpVersionPolicy`` option is intricately linked with the :ref:`config-http-version` setting.
+Therefore, merely specifying ``DownstreamHttpVersion`` may sometimes be inadequate, particularly if your downstream services or Ocelot logs report HTTP connection errors such as ``PROTOCOL_ERROR``.
+In these routes, selecting the precise ``DownstreamHttpVersionPolicy`` value is crucial for the ``HttpVersion`` policy to prevent such protocol errors.
+
+HTTP/2 version policy
+^^^^^^^^^^^^^^^^^^^^^
+
+**Given** you aim to ensure a smooth HTTP/2 connection setup for the Ocelot app and downstream services with SSL enabled:
+
+.. code-block:: json
+
+ {
+ "DownstreamScheme": "https",
+ "DownstreamHttpVersion": "2.0",
+ "DownstreamHttpVersionPolicy": "", // empty
+ "DangerousAcceptAnyServerCertificateValidator": true
+ }
+
+**And** you configure global settings to use Kestrel with this snippet:
+
+.. code-block:: csharp
+
+ var builder = WebApplication.CreateBuilder(args);
+ builder.WebHost.ConfigureKestrel(serverOptions =>
+ {
+ serverOptions.ConfigureEndpointDefaults(listenOptions =>
+ {
+ listenOptions.Protocols = HttpProtocols.Http2;
+ });
+ });
+
+**When** all components are set to communicate exclusively via HTTP/2 without TLS (plain HTTP).
+
+**Then** the downstream services may display error messages such as:
+
+.. code-block::
+
+ HTTP/2 connection error (PROTOCOL_ERROR): Invalid HTTP/2 connection preface
+
+To resolve the issue, ensure that ``HttpRequestMessage`` has its ``VersionPolicy`` set to ``RequestVersionOrHigher``.
+Therefore, the ``DownstreamHttpVersionPolicy`` should be defined as follows:
+
+.. code-block:: json
+
+ {
+ "DownstreamHttpVersion": "2.0",
+ "DownstreamHttpVersionPolicy": "RequestVersionOrHigher" // !
+ }
+
Dependency Injection
--------------------
@@ -416,14 +447,76 @@ You can utilize these methods in the ``ConfigureAppConfiguration`` method (locat
You can find additional details in the dedicated :ref:`di-configuration-overview` section and in subsequent sections related to the :doc:`../features/dependencyinjection` chapter.
+.. _config-route-metadata:
+
+Route Metadata
+--------------
+
+Ocelot provides various features such as routing, authentication, caching, load balancing, and more. However, some users may encounter situations where Ocelot does not meet their specific needs or they want to customize its behavior. In such cases, Ocelot allows users to add metadata to the route configuration. This property can store any arbitrary data that users can access in middlewares or delegating handlers. By using the metadata, users can implement their own logic and extend the functionality of Ocelot.
+
+Here is an example:
+
+.. code-block:: json
+
+ {
+ "Routes": [
+ {
+ "UpstreamHttpMethod": [ "GET" ],
+ "UpstreamPathTemplate": "/posts/{postId}",
+ "DownstreamPathTemplate": "/api/posts/{postId}",
+ "DownstreamHostAndPorts": [
+ { "Host": "localhost", "Port": 80 }
+ ],
+ "Metadata": {
+ "api-id": "FindPost",
+ "my-extension/param1": "overwritten-value",
+ "other-extension/param1": "value1",
+ "other-extension/param2": "value2",
+ "tags": "tag1, tag2, area1, area2, func1",
+ "json": "[1, 2, 3, 4, 5]"
+ }
+ }
+ ],
+ "GlobalConfiguration": {
+ "Metadata": {
+ "instance_name": "dc-1-54abcz",
+ "my-extension/param1": "default-value"
+ }
+ }
+ }
+
+Now, the route metadata can be accessed through the `DownstreamRoute` object:
+
+.. code-block:: csharp
+
+ public static class OcelotMiddlewares
+ {
+ public static Task PreAuthenticationMiddleware(HttpContext context, Func next)
+ {
+ var downstreamRoute = context.Items.DownstreamRoute();
+
+ if(downstreamRoute?.Metadata is {} metadata)
+ {
+ var param1 = metadata.GetValueOrDefault("my-extension/param1") ?? throw new MyExtensionException("Param 1 is null");
+ var param2 = metadata.GetValueOrDefault("my-extension/param2", "custom-value");
+
+ // working with metadata
+ }
+
+ return next();
+ }
+ }
+
""""
.. [#f1] ":ref:`config-merging-files`" feature was requested in `issue 296 `_, since then we extended it in `issue 1216 `_ (PR `1227 `_) as ":ref:`config-merging-tomemory`" subfeature which was released as a part of version `23.2`_.
.. [#f2] ":ref:`config-merging-tomemory`" subfeature is based on the ``MergeOcelotJson`` enumeration type with values: ``ToFile`` and ``ToMemory``. The 1st one is implicit by default, and the second one is exactly what you need when merging to memory. See more details on implementations in the `ConfigurationBuilderExtensions`_ class.
-.. [#f3] :ref:`di-the-addocelot-method` adds default ASP.NET services to DI container. You could call another extended :ref:`di-addocelotusingbuilder-method` while configuring services to develop your own :ref:`di-custom-builder`. See more instructions in the ":ref:`di-addocelotusingbuilder-method`" section of :doc:`../features/dependencyinjection` feature.
-.. [#f4] ":ref:`config-consul-key`" feature was requested in `issue 346 `_ as a part of version `7.0.0 `_.
+.. [#f3] ":ref:`config-version-policy`" feature was requested in `issue 1672 `_ as a part of version `23.3`_.
.. _20.0: https://github.com/ThreeMammals/Ocelot/releases/tag/20.0.0
.. _23.2: https://github.com/ThreeMammals/Ocelot/releases/tag/23.2.0
+.. _23.3: https://github.com/ThreeMammals/Ocelot/releases/tag/23.3.0
.. _ocelot.json: https://github.com/ThreeMammals/Ocelot/blob/main/test/Ocelot.ManualTest/ocelot.json
.. _ConfigurationBuilderExtensions: https://github.com/ThreeMammals/Ocelot/blob/develop/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs
+.. _Consul: https://www.consul.io/
+.. _KV Store: https://developer.hashicorp.com/consul/docs/dynamic-app-config/kv
diff --git a/docs/features/kubernetes.rst b/docs/features/kubernetes.rst
index 44411e589..8bbdb1881 100644
--- a/docs/features/kubernetes.rst
+++ b/docs/features/kubernetes.rst
@@ -73,7 +73,11 @@ The example here shows a typical configuration:
}
Service deployment in **Namespace** ``Dev``, **ServiceDiscoveryProvider** type is ``Kube``, you also can set :ref:`k8s-pollkube-provider` type.
-Note: **Host**, **Port** and **Token** are no longer in use.
+
+ **Note 1**: ``Host``, ``Port`` and ``Token`` are no longer in use.
+
+ **Note 2**: The ``Kube`` provider searches for the service entry using ``ServiceName`` and then retrieves the first available port from the ``EndpointSubsetV1.Ports`` collection.
+ Therefore, if the port name is not specified, the default downstream scheme will be ``http``;
.. _k8s-pollkube-provider:
@@ -99,10 +103,10 @@ This really depends on how volatile your services are.
We doubt it will matter for most people and polling may give a tiny performance improvement over calling Kubernetes per request.
There is no way for Ocelot to work these out for you.
-Global vs Route levels
-^^^^^^^^^^^^^^^^^^^^^^
+Global vs Route Levels
+----------------------
-If your downstream service resides in a different namespace, you can override the global setting at the Route-level by specifying a **ServiceNamespace**:
+If your downstream service resides in a different namespace, you can override the global setting at the Route-level by specifying a ``ServiceNamespace``:
.. code-block:: json
@@ -113,7 +117,47 @@ If your downstream service resides in a different namespace, you can override th
}
]
+.. _k8s-downstream-scheme-vs-port-names:
+
+Downstream Scheme vs Port Names [#f3]_
+--------------------------------------
+
+Kubernetes configuration permits the definition of multiple ports with names for each address of an endpoint subset.
+When binding multiple ports, you assign a name to each subset port.
+To allow the ``Kube`` provider to recognize the desired port by its name, you need to specify the ``DownstreamScheme`` with the port's name;
+if not, the collection's first port entry will be chosen by default.
+
+For instance, consider a service on Kubernetes that exposes two ports: ``https`` for **443** and ``http`` for **80**, as follows:
+
+.. code-block:: text
+
+ Name: my-service
+ Namespace: default
+ Subsets:
+ Addresses: 10.1.161.59
+ Ports:
+ Name Port Protocol
+ ---- ---- --------
+ https 443 TCP
+ http 80 TCP
+
+**When** you need to use the ``http`` port while intentionally bypassing the default ``https`` port (first one),
+you must define ``DownstreamScheme`` to enable the provider to recognize the desired ``http`` port by comparing ``DownstreamScheme`` with the port name as follows:
+
+.. code-block:: json
+
+ "Routes": [
+ {
+ "ServiceName": "my-service",
+ "DownstreamScheme": "http", // port name -> http -> port is 80
+ }
+ ]
+
+**Note**: In the absence of a specified ``DownstreamScheme`` (which is the default behavior), the ``Kube`` provider will select **the first available port** from the ``EndpointSubsetV1.Ports`` collection.
+Consequently, if the port name is not designated, the default downstream scheme utilized will be ``http``.
+
""""
.. [#f1] `Wikipedia `_ | `K8s Website `_ | `K8s Documentation `_ | `K8s GitHub `_
.. [#f2] This feature was requested as part of `issue 345 `_ to add support for `Kubernetes `_ :doc:`../features/servicediscovery` provider.
+.. [#f3] *"Downstream Scheme vs Port Names"* feature was requested as part of `issue 1967 `_ and released in version `23.3 `_
diff --git a/docs/features/metadata.rst b/docs/features/metadata.rst
new file mode 100644
index 000000000..902ff972e
--- /dev/null
+++ b/docs/features/metadata.rst
@@ -0,0 +1,99 @@
+Metadata
+========
+
+Configuration
+-------------
+
+Ocelot provides various features such as routing, authentication, caching, load
+balancing, and more.
+However, some users may encounter situations where Ocelot does not meet their
+specific needs or they want to customize its behavior.
+In such cases, Ocelot allows users to add metadata to the route configuration.
+This property can store any arbitrary data that users can access in middlewares
+or delegating handlers.
+
+By using the metadata, users can implement their own logic and extend the
+functionality of Ocelot e.g.
+
+.. code-block:: json
+
+ {
+ "Routes": [
+ {
+ "UpstreamHttpMethod": [ "GET" ],
+ "UpstreamPathTemplate": "/posts/{postId}",
+ "DownstreamPathTemplate": "/api/posts/{postId}",
+ "DownstreamHostAndPorts": [
+ { "Host": "localhost", "Port": 80 }
+ ],
+ "Metadata": {
+ "id": "FindPost",
+ "tags": "tag1, tag2, area1, area2, func1",
+ "plugin1.enabled": "true",
+ "plugin1.values": "[1, 2, 3, 4, 5]",
+ "plugin1.param": "value2",
+ "plugin1.param2": "123",
+ "plugin2/param1": "overwritten-value",
+ "plugin2/param2": "{\"name\":\"John Doe\",\"age\":30,\"city\":\"New York\",\"is_student\":false,\"hobbies\":[\"reading\",\"hiking\",\"cooking\"]}"
+ }
+ }
+ ],
+ "GlobalConfiguration": {
+ "Metadata": {
+ "instance_name": "machine-1",
+ "plugin2/param1": "default-value"
+ }
+ }
+ }
+
+Now, the route metadata can be accessed through the ``DownstreamRoute`` object:
+
+.. code-block:: csharp
+
+ public class MyMiddleware
+ {
+ public Task Invoke(HttpContext context, Func next)
+ {
+ var route = context.Items.DownstreamRoute();
+ var enabled = route.GetMetadata("plugin1.enabled");
+ var values = route.GetMetadata("plugin1.values");
+ var param1 = route.GetMetadata("plugin1.param", "system-default-value");
+ var param2 = route.GetMetadata("plugin1.param2");
+
+ // working on the plugin1's function
+
+ return next?.Invoke();
+ }
+ }
+
+Extension Methods
+-----------------
+
+Ocelot provides one DowstreamRoute extension method to help you retrieve your metadata values effortlessly.
+With the exception of the types string, bool, bool?, string[] and numeric, all strings passed as parameters are treated as json strings and an attempt is made to convert them into objects of generic type T.
+If the value is null, then, if not explicitely specified, the default for the chosen target type is returned.
+
+.. list-table::
+ :widths: 20 40 40
+
+ * - Method
+ - Description
+ - Notes
+ * - ``GetMetadata``
+ - The metadata value is returned as string without further parsing
+ -
+ * - ``GetMetadata``
+ - The metadata value is splitted by a given separator (default ``,``) and returned as a string array.
+ - Several parameters can be set in the global configuration, such as Separators (default = ``[","]``), StringSplitOptions (default ``None``) and TrimChars, the characters that should be trimmed (default = ``[' ']``).
+ * - ``GetMetadata``
+ - The metadata value is parsed to a number.
+ - Some parameters can be set in the global configuration, such as NumberStyle (default ``Any``) and CurrentCulture (default ``CultureInfo.CurrentCulture``)
+ * - ``GetMetadata``
+ - The metadata value is converted to the given generic type. The value is treated as a json string and the json serializer tries to deserialize the string to the target type.
+ - A JsonSerializerOptions object can be passed as method parameter, Web is used as default.
+ * - ``GetMetadata``
+ - Check if the metadata value is a truthy value, otherwise return false.
+ - The truthy values are: ``true``, ``yes``, ``ok``, ``on``, ``enable``, ``enabled``
+ * - ``GetMetadata``
+ - Check if the metadata value is a truthy value (return true), or falsy value (return false), otherwise return null.
+ - The known truthy values are: ``true``, ``yes``, ``ok``, ``on``, ``enable``, ``enabled``, ``1``, the known falsy values are: ``false``, ``no``, ``off``, ``disable``, ``disabled``, ``0``
diff --git a/docs/features/qualityofservice.rst b/docs/features/qualityofservice.rst
index 799efdc08..fde699ddf 100644
--- a/docs/features/qualityofservice.rst
+++ b/docs/features/qualityofservice.rst
@@ -3,22 +3,31 @@ Quality of Service
Label: `QoS `_
-Ocelot supports one QoS capability at the current time. You can set on a per Route basis if you want to use a circuit breaker when making requests to a downstream service.
-This uses an awesome .NET library called `Polly`_, check them out `in official repository `_.
+Ocelot currently supports a single **QoS** capability.
+It allows you to configure, on a per-route basis, the use of a circuit breaker when making requests to downstream services.
+This feature leverages a superb .NET library known as `Polly`_. For more information, visit their `official repository `_.
-The first thing you need to do if you want to use the :doc:`../features/administration` API is bring in the relevant NuGet `package `_:
+Installation
+------------
+
+To use the :doc:`../features/administration` API, the first step is to import the relevant NuGet `package `_:
.. code-block:: powershell
Install-Package Ocelot.Provider.Polly
-Then in your ``ConfigureServices`` method to add `Polly`_ services we must call the ``AddPolly()`` extension of the ``OcelotBuilder`` being returned by ``AddOcelot()`` [#f1]_ like below:
+Next, within your ``ConfigureServices`` method, to incorporate `Polly`_ services, invoke the ``AddPolly()`` extension on the ``OcelotBuilder`` returned by ``AddOcelot()`` [#f1]_ as shown below:
.. code-block:: csharp
services.AddOcelot()
.AddPolly();
+.. _qos-configuration:
+
+Configuration
+-------------
+
Then add the following section to a Route configuration:
.. code-block:: json
@@ -29,44 +38,64 @@ Then add the following section to a Route configuration:
"TimeoutValue": 5000
}
-- You must set a number equal or greater than ``2`` against **ExceptionsAllowedBeforeBreaking** for this rule to be implemented. [#f2]_
-- **DurationOfBreak** means the circuit breaker will stay open for 1 second after it is tripped.
-- **TimeoutValue** means if a request takes more than 5 seconds, it will automatically be timed out.
+- You must set a number equal or greater than ``2`` against ``ExceptionsAllowedBeforeBreaking`` for this rule to be implemented. [#f2]_
+- ``DurationOfBreak`` means the circuit breaker will stay open for 1 second after it is tripped.
+- ``TimeoutValue`` means if a request takes more than 5 seconds, it will automatically be timed out.
-You can set the **TimeoutValue** in isolation of the **ExceptionsAllowedBeforeBreaking** and **DurationOfBreak** options:
+.. _qos-circuit-breaker-strategy:
+
+Circuit Breaker strategy
+------------------------
+
+The options ``ExceptionsAllowedBeforeBreaking`` and ``DurationOfBreak`` can be configured independently of ``TimeoutValue``:
.. code-block:: json
"QoSOptions": {
- "TimeoutValue": 5000
+ "ExceptionsAllowedBeforeBreaking": 3,
+ "DurationOfBreak": 1000
}
-There is no point setting the other two in isolation as they affect each other!
+Alternatively, you may omit ``DurationOfBreak`` to default to the implicit 5 seconds as per Polly `documentation `_:
-Defaults
---------
+.. code-block:: json
-If you do not add a QoS section, QoS will not be used, however Ocelot will default to a **90** seconds timeout on all downstream requests.
-If someone needs this to be configurable, open an issue. [#f2]_
+ "QoSOptions": {
+ "ExceptionsAllowedBeforeBreaking": 3
+ }
-.. _qos-polly-v7-vs-v8:
+This setup activates only the `Circuit breaker `_ strategy.
-`Polly`_ v7 vs v8
------------------
+.. _qos-timeout-strategy:
-Important changes in version `23.2`_: [#f3]_
+Timeout strategy
+----------------
- - With `Polly`_ version 8+, the ``ExceptionsAllowedBeforeBreaking`` value must be equal to or greater than **2**!
- - The ``AddPolly`` method has been migrated from v7 policy wrappers to v8 resilience pipelines. Consequently, it now exhibits different behavior based on v8 pipelines.
+The ``TimeoutValue`` can be configured independently from the ``ExceptionsAllowedBeforeBreaking`` and ``DurationOfBreak`` settings:
-If you prefer not to modify your settings, you can continue using `Polly`_ v7 as follows:
+.. code-block:: json
-.. code-block:: csharp
+ "QoSOptions": {
+ "TimeoutValue": 5000
+ }
- services.AddOcelot()
- .AddPollyV7();
+This setup activates only the `Timeout `_ strategy.
+
+Notes
+-----
+
+1. Without a QoS section, QoS will not be utilized, and Ocelot will impose a default timeout of **90** seconds for all downstream requests.
+ To request configurability, please open an issue. [#f2]_
+
+2. `Polly`_ V7 syntax is no longer supported as of version `23.2`_. [#f3]_
+
+3. For `Polly`_ version 8 and above, the following constraints on values are specified in `the documentation `_:
+
+ * The ``ExceptionsAllowedBeforeBreaking`` value must be **2** or higher.
+ * The ``DurationOfBreak`` value must exceed **500** milliseconds, defaulting to **5000** milliseconds (5 seconds) if unspecified or if the value is **500** milliseconds or less.
+ * The ``TimeoutValue`` must be over **10** milliseconds.
-**Note**: Support for `Polly`_ v7 will be removed in a future version. We recommend avoiding this method (which is tagged as ``Obsolete``) unless absolutely necessary.
+ Consult the `Resilience strategies `_ documentation for a detailed understanding of each option.
.. _qos-extensibility:
diff --git a/docs/features/ratelimiting.rst b/docs/features/ratelimiting.rst
index 9a69f4ded..94db1db5d 100644
--- a/docs/features/ratelimiting.rst
+++ b/docs/features/ratelimiting.rst
@@ -1,35 +1,50 @@
Rate Limiting
=============
+`What's rate limiting? `_
+
+* `Rate limiting | Wikipedia `_
+* `Rate Limiting pattern | Azure Architecture Center | Microsoft Learn `_
+* `Rate Limiting | Ask Google `_
+
Ocelot Own Implementation
-------------------------
-Ocelot supports rate limiting of upstream requests so that your downstream services do not become overloaded.
+Ocelot provides *rate limiting* for upstream requests to prevent downstream services from becoming overwhelmed. [#f1]_
-The authors of this feature were inspired by `@catcherwong article `_ to finally write this documentation.
-This feature was added by `@geffzhang `_ on GitHub! Thanks very much!
+Rate Limit by Client's Header
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-To get rate limiting working for a Route you need to add the following JSON to it:
+To implement *rate limiting* for a Route, you need to incorporate the following JSON configuration:
.. code-block:: json
"RateLimitOptions": {
- "ClientWhitelist": [],
+ "ClientWhitelist": [], // array of strings
"EnableRateLimiting": true,
- "Period": "1s",
- "PeriodTimespan": 1,
+ "Period": "1s", // seconds, minutes, hours, days
+ "PeriodTimespan": 1, // only seconds
"Limit": 1
}
-* **ClientWhitelist** - This is an array that contains the whitelist of the client.
- It means that the client in this array will not be affected by the rate limiting.
-* **EnableRateLimiting** - This value specifies enable endpoint rate limiting.
-* **Period** - This value specifies the period that the limit applies to, such as ``1s``, ``5m``, ``1h``, ``1d`` and so on.
- If you make more requests in the period than the limit allows then you need to wait for **PeriodTimespan** to elapse before you make another request.
-* **PeriodTimespan** - This value specifies that we can retry after a certain number of seconds.
-* **Limit** - This value specifies the maximum number of requests that a client can make in a defined period.
+* **ClientWhitelist** - An array containing the whitelisted clients. Clients listed here will be exempt from rate limiting.
+ For more information on the **ClientIdHeader** option, refer to the :ref:`rl-global-configuration` section.
+* **EnableRateLimiting** - This setting enables rate limiting on endpoints.
+* **Period** - This parameter defines the duration for which the limit is applicable, such as ``1s`` (seconds), ``5m`` (minutes), ``1h`` (hours), and ``1d`` (days).
+ If you reach the exact **Limit** of requests, the excess occurs immediately, and the **PeriodTimespan** begins.
+ You must wait for the **PeriodTimespan** duration to pass before making another request.
+ Should you exceed the number of requests within the period more than the **Limit** permits, the **QuotaExceededMessage** will appear in the response, accompanied by the **HttpStatusCode**.
+* **PeriodTimespan** - This parameter indicates the time in **seconds** after which a retry is permissible.
+ During this interval, the **QuotaExceededMessage** will appear in the response, accompanied by an **HttpStatusCode**.
+ Clients are advised to consult the ``Retry-After`` header to determine the timing of subsequent requests.
+* **Limit** - This parameter defines the upper limit of requests a client is allowed to make within a specified **Period**.
+
+.. _rl-global-configuration:
+
+Global Configuration
+^^^^^^^^^^^^^^^^^^^^
-You can also set the following in the **GlobalConfiguration** part of **ocelot.json**:
+You can set the following in the ``GlobalConfiguration`` section of `ocelot.json`_:
.. code-block:: json
@@ -38,33 +53,48 @@ You can also set the following in the **GlobalConfiguration** part of **ocelot.j
"RateLimitOptions": {
"DisableRateLimitHeaders": false,
"QuotaExceededMessage": "Customize Tips!",
- "HttpStatusCode": 123,
- "ClientIdHeader": "Test"
+ "HttpStatusCode": 418, // I'm a teapot
+ "ClientIdHeader": "MyRateLimiting"
}
}
-* **DisableRateLimitHeaders** - This value specifies whether ``X-Rate-Limit`` and ``Retry-After`` headers are disabled.
-* **QuotaExceededMessage** - This value specifies the exceeded message.
-* **HttpStatusCode** - This value specifies the returned HTTP status code when rate limiting occurs.
-* **ClientIdHeader** - Allows you to specifiy the header that should be used to identify clients. By default it is ``ClientId``
+* **DisableRateLimitHeaders** - Determines if the ``X-Rate-Limit`` and ``Retry-After`` headers are disabled.
+* **QuotaExceededMessage** - Defines the message displayed when the quota is exceeded. It is optional and the default message is informative.
+* **HttpStatusCode** - Indicates the HTTP status code returned during *rate limiting*. The default value is **429** (`Too Many Requests`_).
+* **ClientIdHeader** - Specifies the header used to identify clients, with ``ClientId`` as the default.
Future and ASP.NET Core Implementation
--------------------------------------
-The Ocelot team considers to redesign *Rate Limiting* feature,
-because of `Announcing Rate Limiting for .NET `_ by Brennan Conroy on July 13th, 2022.
-There is no decision at the moment, and the old version of the feature is included as a part of release `20.0 `_ for .NET 7.
+The Ocelot team is contemplating a redesign of the *Rate Limiting* feature following the `Announcing Rate Limiting for .NET`_ by Brennan Conroy on July 13th, 2022.
+Currently, no decision has been made, and the previous version of the feature remains part of the `20.0`_ release for .NET 7. [#f2]_
-See more about new feature being added into ASP.NET Core 7.0 release:
+Discover the new features being introduced in the ASP.NET Core 7.0 release:
-* `RateLimiter Class `_, since ASP.NET Core **7.0**
-* `System.Threading.RateLimiting `_ NuGet package
-* `Rate limiting middleware in ASP.NET Core `_ article by Arvin Kahbazi, Maarten Balliauw, and Rick Anderson
+* The `RateLimiter Class `_, available since ASP.NET Core 7.0
+* The `System.Threading.RateLimiting `_ NuGet package
+* The `Rate limiting middleware in ASP.NET Core `_ article by Arvin Kahbazi, Maarten Balliauw, and Rick Anderson
-However, it makes sense to keep the old implementation as a Ocelot built-in native feature, but we are going to migrate to the new Rate Limiter from ``Microsoft.AspNetCore.RateLimiting`` namespace.
+While retaining the old implementation as an Ocelot built-in feature makes sense, we plan to transition to the new Rate Limiter from the ``Microsoft.AspNetCore.RateLimiting`` namespace.
+Please share your thoughts with us in the `Discussions `_ space of the repository. |octocat|
+
+""""
+
+.. [#f1] Historically, the *"Ocelot Own Rate Limiting"* feature is one of the oldest and first features of Ocelot. This feature was delivered in PR `37`_ by `@geffzhang`_ on GitHub. Many thanks! It was initially released in version `1.3.2`_. The authors were inspired by `@catcherwong article`_ to write this documentation.
+.. [#f2] Since PR `37`_ and version `1.3.2`_, the Ocelot team has reviewed and redesigned the feature to provide stable behavior. The fix for bug `1590`_ (PR `1592`_) was released as part of version `23.3`_.
+
+.. _Announcing Rate Limiting for .NET: https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/
+.. _ocelot.json: https://github.com/ThreeMammals/Ocelot/blob/main/test/Ocelot.ManualTest/ocelot.json
+.. _@geffzhang: https://github.com/ThreeMammals/Ocelot/commits?author=geffzhang
+.. _@catcherwong article: http://www.c-sharpcorner.com/article/building-api-gateway-using-ocelot-in-asp-net-core-rate-limiting-part-four/
+.. _Too Many Requests: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429
+.. _37: https://github.com/ThreeMammals/Ocelot/pull/37
+.. _1590: https://github.com/ThreeMammals/Ocelot/issues/1590
+.. _1592: https://github.com/ThreeMammals/Ocelot/pull/1592
+.. _1.3.2: https://github.com/ThreeMammals/Ocelot/releases/tag/1.3.2
+.. _20.0: https://github.com/ThreeMammals/Ocelot/releases/tag/20.0.0
+.. _23.3: https://github.com/ThreeMammals/Ocelot/releases/tag/23.3.0
.. |octocat| image:: https://github.githubassets.com/images/icons/emoji/octocat.png
:alt: octocat
:width: 23
-
-Please, share your opinion to us in the `Discussions `_ space of the repository. |octocat|
diff --git a/docs/features/requestaggregation.rst b/docs/features/requestaggregation.rst
index f1123cf19..c9a642388 100644
--- a/docs/features/requestaggregation.rst
+++ b/docs/features/requestaggregation.rst
@@ -217,9 +217,12 @@ Below is an example of an aggregator that you could implement for your solution:
Gotchas
-------
-You cannot use Routes with specific **RequestIdKeys** as this would be crazy complicated to track.
+* You cannot use Routes with specific **RequestIdKeys** as this would be crazy complicated to track.
+* Aggregation only supports the ``GET`` HTTP verb.
+* Aggregation allows for the forwarding of ``HttpRequest.Body`` to downstream services by duplicating the body data.
+ Form data and attached files should also be forwarded.
+ It is essential to always specify the ``Content-Length`` header in requests to upstream; otherwise, Ocelot will log warnings like *"Aggregation does not support body copy without Content-Length header!"*.
-Aggregation only supports the ``GET`` HTTP verb.
""""
diff --git a/docs/features/routing.rst b/docs/features/routing.rst
index 6de03d8e2..825f5d9e1 100644
--- a/docs/features/routing.rst
+++ b/docs/features/routing.rst
@@ -154,11 +154,63 @@ The Route above will only be matched when the ``Host`` header value is ``somedom
If you do not set **UpstreamHost** on a Route then any ``Host`` header will match it.
This means that if you have two Routes that are the same, apart from the **UpstreamHost**, where one is null and the other set Ocelot will favour the one that has been set.
+.. _routing-upstream-headers:
+
+Upstream Headers [#f3]_
+-----------------------
+
+In addition to routing by ``UpstreamPathTemplate``, you can also define ``UpstreamHeaderTemplates``.
+For a route to match, all headers specified in this dictionary object must be present in the request headers.
+
+.. code-block:: json
+
+ {
+ // ...
+ "UpstreamPathTemplate": "/",
+ "UpstreamHttpMethod": [ "Get" ],
+ "UpstreamHeaderTemplates": { // dictionary
+ "country": "uk", // 1st header
+ "version": "v1" // 2nd header
+ }
+ }
+
+In this scenario, the route will only match if a request includes both headers with the specified values.
+
+Header placeholders
+^^^^^^^^^^^^^^^^^^^
+
+Let's explore a more intriguing scenario where placeholders can be effectively utilized within your ``UpstreamHeaderTemplates``.
+
+Consider the following approach using the special placeholder format ``{header:placeholdername}``:
+
+.. code-block:: json
+
+ {
+ "DownstreamPathTemplate": "/{versionnumber}/api", // with placeholder
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ { "Host": "10.0.10.1", "Port": 80 }
+ ],
+ "UpstreamPathTemplate": "/api",
+ "UpstreamHttpMethod": [ "Get" ],
+ "UpstreamHeaderTemplates": {
+ "version": "{header:versionnumber}" // 'header:' prefix vs placeholder
+ }
+ }
+
+In this scenario, the entire value of the request header "**version**" is inserted into the ``DownstreamPathTemplate``.
+If necessary, a more intricate upstream header template can be specified, using placeholders such as ``version-{header:version}_country-{header:country}``.
+
+ **Note 1**: Placeholders are not required in ``DownstreamPathTemplate``.
+ This scenario can be utilized to mandate a specific header regardless of its value.
+
+ **Note 2**: Additionally, the ``UpstreamHeaderTemplates`` dictionary options are applicable for :doc:`../features/requestaggregation` as well.
+
Priority
--------
You can define the order you want your Routes to match the Upstream ``HttpRequest`` by including a **Priority** property in **ocelot.json**.
-See `issue 270 `_ for reference.
+See issue `270`_ for reference.
.. code-block:: json
@@ -195,7 +247,7 @@ Query String Placeholders
In addition to URL path :ref:`routing-placeholders` Ocelot is able to forward query string parameters with their processing in the form of ``{something}``.
Also, the query parameter placeholder needs to be present in both the **DownstreamPathTemplate** and **UpstreamPathTemplate** properties.
-Placeholder replacement works bi-directionally between path and query strings, with some `restrictions <#restrictions-on-use>`_ on usage.
+Placeholder replacement works bi-directionally between path and query strings, with some restrictions on usage (see :ref:`routing-merging-of-query-parameters`).
Path to Query String direction
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -209,7 +261,9 @@ Ocelot allows you to specify a query string as part of the **DownstreamPathTempl
"DownstreamPathTemplate": "/api/subscriptions/{subscription}/updates?unitId={unit}",
}
-In this example Ocelot will use the value from the ``{unit}`` placeholder in the upstream path template and add it to the downstream request as a query string parameter called ``unitId``! Make sure you name the placeholder differently due to `restrictions <#restrictions-on-use>`_ on usage.
+In this example Ocelot will use the value from the ``{unit}`` placeholder in the upstream path template and add it to the downstream request as a query string parameter called ``unitId``!
+
+ Note! Make sure you name the placeholder differently due to :ref:`routing-merging-of-query-parameters`.
Query String to Path direction
@@ -227,7 +281,10 @@ Ocelot will also allow you to put query string parameters in the **UpstreamPathT
In this example Ocelot will only match requests that have a matching URL path and the query string starts with ``unitId=something``.
You can have other queries after this but you must start with the matching parameter.
Also Ocelot will swap the ``{uid}`` parameter from the query string and use it in the downstream request path.
-Note, the best practice is giving different placeholder name than the name of query parameter due to `restrictions <#restrictions-on-use>`_ on usage.
+
+ Note, the best practice is giving different placeholder name than the name of query parameter due to :ref:`routing-merging-of-query-parameters`.
+
+.. _routing-catch-all-query-string:
Catch All Query String
^^^^^^^^^^^^^^^^^^^^^^
@@ -243,58 +300,72 @@ The placeholder ``{everything}`` name does not matter, any name will work.
}
This entire query string routing feature is very useful in cases where the query string should not be transformed but rather routed without any changes,
-such as OData filters and etc (see issue `1174 `_).
+such as OData filters and etc (see issue `1174`_).
-**Note**, the ``{everything}`` placeholder can be empty while catching all query strings, because this is a part of the :ref:`routing-empty-placeholders` feature! [#f1]_
-Thus, upstream paths ``/contracts?`` and ``/contracts`` are routed to downstream path ``/apipath/contracts``, which has no query string at all.
+ **Note**, the ``{everything}`` placeholder can be empty while catching all query strings, because this is a part of the :ref:`routing-empty-placeholders` feature! [#f1]_
+ Thus, upstream paths ``/contracts?`` and ``/contracts`` are routed to downstream path ``/apipath/contracts``, which has no query string at all.
-Restrictions on use
-^^^^^^^^^^^^^^^^^^^
+.. _routing-merging-of-query-parameters:
+
+Merging of Query Parameters
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Query string parameters are unsorted and merged to create the final downstream URL.
+This process is essential as the ``DownstreamUrlCreatorMiddleware`` requires control over placeholder replacement and the merging of duplicate parameters.
+A parameter that appears first in the **UpstreamPathTemplate** may occupy a different position in the final downstream URL.
+Furthermore, if the **DownstreamPathTemplate** includes query parameters at the beginning, their position in the **UpstreamPathTemplate** will be indeterminate unless explicitly defined.
+
+In a typical scenario, the merging algorithm constructs the final downstream URL query string by:
-The query string parameters are ordered and merged to produce the final downstream URL.
-This is necessary because the ``DownstreamUrlCreatorMiddleware`` needs to have some control when replacing placeholders and merging duplicate parameters.
-So, even if your parameter is presented as the first parameter in the upstream, then in the final downstream URL the said query parameter will have a different position.
-But this doesn't seem to break anything in the downstream API.
+1. Taking the initially defined query parameters in **DownstreamPathTemplate** and placing them at the beginning, with any necessary placeholder replacements.
+2. Adding all parameters from the :ref:`routing-catch-all-query-string`, represented by the placeholder ``{everything}``, into the second position (following the explicitly defined parameters from **step 1**).
+3. Appending any remaining replaced placeholder values as parameter values to the end of the string, if they are present.
-Because of parameters merging, special ASP.NET API `model binding `_
-for arrays is not supported if you use array items representation like ``selectedCourses=1050&selectedCourses=2000``.
-This query string will be merged as ``selectedCourses=1050`` in downstream URL. So, array data will be lost!
-Make sure upstream clients generate correct query string for array models like ``selectedCourses[0]=1050&selectedCourses[1]=2000``.
-To understand array model bidings, see `Bind arrays and string values from headers and query strings `_ docs.
+Array parameters in ASP.NET API's model binding
+"""""""""""""""""""""""""""""""""""""""""""""""
-**Warning!** Query string placeholders have naming restrictions due to ``DownstreamUrlCreatorMiddleware`` implementations.
-On the other hand, it gives you the flexibility to control whether the parameter is present in the final downstream URL.
-Here are two user scenarios.
+Due to parameters merging, ASP.NET API's special `model binding`_ for arrays **is not supported** having the array item representation format of ``selectedCourses=1050&selectedCourses=2000``.
+This query string will be merged into ``selectedCourses=1050`` in the downstream URL, resulting in the loss of array data.
+It is crucial for upstream clients to generate the correct query string for array models, such as ``selectedCourses[0]=1050&selectedCourses[1]=2000``.
+For a comprehensive understanding of array model bindings, refer to the documentation: `Bind arrays and string values from headers and query strings`_.
-* User wants to save the parameter after replacing the placeholder (see issue `473 `_).
- To do this you need to use the following template definition:
+Control over parameter existence
+""""""""""""""""""""""""""""""""
- .. code-block:: json
+Be aware that query string placeholders are subject to naming restrictions due to the ``DownstreamUrlCreatorMiddleware``'s merging algorithm implementation.
+However, this also provides the flexibility to manage the presence of parameters in the final downstream URL by their names.
+
+Consider the following 2 development scenarios :htm:`→`
+
+1. A developer wishes **to preserve a parameter** after substituting a placeholder (refer to issue `473`_).
+ This requires the use of the template definition below:
+
+ .. code-block:: json
- {
- "UpstreamPathTemplate": "/path/{serverId}/{action}",
- "DownstreamPathTemplate": "/path2/{action}?server={serverId}"
- }
+ {
+ "UpstreamPathTemplate": "/path/{serverId}/{action}",
+ "DownstreamPathTemplate": "/path2/{action}?server={serverId}"
+ }
- So, ``{serverId}`` placeholder and ``server`` parameter **names are different**!
- Finally, the ``server`` parameter is kept.
+ | Here, the ``{serverId}`` placeholder and the ``server`` parameter **names differ**! Ultimately, the ``server`` parameter is retained.
+ | It is important to note that due to the case-sensitive comparison of names, the ``server`` parameter will not be preserved with the ``{server}`` placeholder. However, using the ``{Server}`` placeholder is acceptable for retaining the parameter.
-* User wants to remove old parameter after replacing placeholder (see issue `952 `_).
- To do this you need to use the same names:
+2. The developer intends **to remove an outdated parameter** after substituting a placeholder (refer to issue `952`_).
+ For this action, you must use identical names having the case-sensitive comparison:
- .. code-block:: json
+ .. code-block:: json
- {
- "UpstreamPathTemplate": "/users?userId={userId}",
- "DownstreamPathTemplate": "/persons?personId={userId}"
- }
+ {
+ "UpstreamPathTemplate": "/users?userId={userId}",
+ "DownstreamPathTemplate": "/persons?personId={userId}"
+ }
- So, both ``{userId}`` placeholder and ``userId`` parameter **names are the same**!
- Finally, the ``userId`` parameter is removed.
+ | Thus, the ``{userId}`` placeholder and the ``userId`` parameter **have identical names**! Subsequently, the ``userId`` parameter is eliminated.
+ | Be aware that due to the case sensitive nature of the comparison, if the ``{userid}`` placeholder is used, the ``userId`` parameter will not be removed!
.. _routing-security-options:
-Security Options [#f3]_
+Security Options [#f4]_
-----------------------
Ocelot allows you to manage multiple patterns for allowed/blocked IPs using the `IPAddressRange `_ package
@@ -326,7 +397,7 @@ The current patterns managed are the following:
.. _routing-dynamic:
-Dynamic Routing [#f4]_
+Dynamic Routing [#f5]_
----------------------
The idea is to enable dynamic routing when using a :doc:`../features/servicediscovery` provider so you don't have to provide the Route config.
@@ -336,5 +407,13 @@ See the :ref:`sd-dynamic-routing` docs if this sounds interesting to you.
.. [#f1] ":ref:`routing-empty-placeholders`" feature is available starting in version `23.0 `_, see issue `748 `_ and the `23.0 `__ release notes for details.
.. [#f2] ":ref:`routing-upstream-host`" feature was requested as part of `issue 216 `_.
-.. [#f3] ":ref:`routing-security-options`" feature was requested as part of `issue 628 `_ (of `12.0.1 `_ version), then redesigned and improved by `issue 1400 `_, and published in version `20.0 `_ docs.
-.. [#f4] ":ref:`routing-dynamic`" feature was requested as part of `issue 340 `_. Complete reference: :ref:`sd-dynamic-routing`.
+.. [#f3] ":ref:`routing-upstream-headers`" feature was proposed in `issue 360 `_, and released in version `24.0 `_.
+.. [#f4] ":ref:`routing-security-options`" feature was requested as part of `issue 628 `_ (of `12.0.1 `_ version), then redesigned and improved by `issue 1400 `_, and published in version `20.0 `_ docs.
+.. [#f5] ":ref:`routing-dynamic`" feature was requested as part of `issue 340 `_. Complete reference: :ref:`sd-dynamic-routing`.
+
+.. _model binding: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-8.0#collections
+.. _Bind arrays and string values from headers and query strings: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/parameter-binding?view=aspnetcore-8.0#bind-arrays-and-string-values-from-headers-and-query-strings
+.. _270: https://github.com/ThreeMammals/Ocelot/issues/270
+.. _473: https://github.com/ThreeMammals/Ocelot/issues/473
+.. _952: https://github.com/ThreeMammals/Ocelot/issues/952
+.. _1174: https://github.com/ThreeMammals/Ocelot/issues/1174
diff --git a/docs/features/servicediscovery.rst b/docs/features/servicediscovery.rst
index 6d9d8d4ef..a4e4f96c5 100644
--- a/docs/features/servicediscovery.rst
+++ b/docs/features/servicediscovery.rst
@@ -9,28 +9,85 @@ At the moment this is only supported in the **GlobalConfiguration** section, whi
Consul
------
- | **Namespace**: `Ocelot.Provider.Consul `_
+ | **Namespace**: ``Ocelot.Provider.Consul``
-The first thing you need to do is install the `Ocelot.Provider.Consul `__ package that provides `Consul `_ support in Ocelot:
+The first thing you need to do is install the `Ocelot.Provider.Consul `_ package that provides `Consul`_ support in Ocelot:
.. code-block:: powershell
Install-Package Ocelot.Provider.Consul
-Then add the following to your ``ConfigureServices`` method:
+To register *Consul* services, you must invoke the ``AddConsul()`` extension using the ``OcelotBuilder`` returned by ``AddOcelot()`` [#f1]_.
+Therefore, include the following in your ``ConfigureServices`` method:
.. code-block:: csharp
services.AddOcelot()
- .AddConsul();
+ .AddConsul(); // or .AddConsul()
-Currently there are 2 types of Consul *service discovery* providers: ``Consul`` and ``PollConsul``.
-The default provider is ``Consul``, which means that if ``ConsulProviderFactory`` cannot read, understand, or parse the **Type** property of the ``ServiceProviderConfiguration`` object, then a ``Consul`` provider instance is created by the factory.
+Currently there are 2 types of *Consul* service discovery providers: ``Consul`` and ``PollConsul``.
+The default provider is ``Consul``, which means that if ``ConsulProviderFactory`` cannot read, understand, or parse the **Type** property of the ``ServiceProviderConfiguration`` object,
+then a :ref:`sd-consul-provider` instance is created by the factory.
-Explore these types of providers and understand the differences in the subsections below.
+Explore these types of providers and understand the differences in the subsections: :ref:`sd-consul-provider` and :ref:`sd-pollconsul-provider`.
-Consul Provider Type
-^^^^^^^^^^^^^^^^^^^^
+.. _sd-consul-configuration-in-kv:
+
+Configuration in `KV Store`_
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Add the following when you register your services Ocelot will attempt to store and retrieve its :doc:`../features/configuration` in *Consul* `KV Store`_:
+
+.. code-block:: csharp
+
+ services.AddOcelot()
+ .AddConsul()
+ .AddConfigStoredInConsul(); // !
+
+You also need to add the following to your `ocelot.json`_.
+This is how Ocelot finds your *Consul* agent and interacts to load and store the configuration from *Consul*.
+
+.. code-block:: json
+
+ "GlobalConfiguration": {
+ "ServiceDiscoveryProvider": {
+ "Host": "localhost",
+ "Port": 9500
+ }
+ }
+
+The team decided to create this feature after working on the Raft consensus algorithm and finding out its super hard.
+Why not take advantage of the fact Consul already gives you this!
+We guess it means if you want to use Ocelot to its fullest, you take on Consul as a dependency for now.
+
+ **Note!** This feature has a `3 seconds TTL`_ cache before making a new request to your local *Consul* agent.
+
+.. _sd-consul-configuration-key:
+
+Consul Configuration Key [#f2]_
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you are using *Consul* for :doc:`../features/configuration` (or other providers in the future), you might want to key your configurations: so you can have multiple configurations.
+
+In order to specify the key you need to set the **ConfigurationKey** property in the **ServiceDiscoveryProvider** options of the configuration JSON file e.g.
+
+.. code-block:: json
+
+ "GlobalConfiguration": {
+ "ServiceDiscoveryProvider": {
+ "Host": "localhost",
+ "Port": 9500,
+ "ConfigurationKey": "Ocelot_A" // !
+ }
+ }
+
+In this example Ocelot will use ``Ocelot_A`` as the key for your configuration when looking it up in *Consul*.
+If you do not set the **ConfigurationKey**, Ocelot will use the string ``InternalConfiguration`` as the key.
+
+.. _sd-consul-provider:
+
+``Consul`` Provider
+^^^^^^^^^^^^^^^^^^^
| **Class**: `Ocelot.Provider.Consul.Consul `_
@@ -67,8 +124,10 @@ If no load balancer is specified, Ocelot will not load balance requests.
When this is set up Ocelot will lookup the downstream host and port from the *service discovery* provider and load balance requests across any available services.
-PollConsul Provider Type
-^^^^^^^^^^^^^^^^^^^^^^^^
+.. _sd-pollconsul-provider:
+
+``PollConsul`` Provider
+^^^^^^^^^^^^^^^^^^^^^^^
| **Class**: `Ocelot.Provider.Consul.PollConsul `_
@@ -98,7 +157,7 @@ Service Definition
Your services need to be added to Consul something like below (C# style but hopefully this make sense)...
The only important thing to note is not to add ``http`` or ``https`` to the ``Address`` field.
We have been contacted before about not accepting scheme in ``Address``.
-After reading `this `_ we do not think the scheme should be in there.
+After reading `Agents Overview `_ and `Define services `_ docs we do not think the **scheme** should be in there.
In C#
@@ -140,6 +199,68 @@ In order so this to work you must add the additional property below:
Ocelot will add this token to the Consul client that it uses to make requests and that is then used for every request.
+.. _sd-consul-service-builder:
+
+Consul Service Builder [#f3]_
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ | **Interface**: ``IConsulServiceBuilder``
+ | **Implementation**: ``DefaultConsulServiceBuilder``
+
+The Ocelot community has consistently reported, both in the past and presently, issues with *Consul* services (such as connectivity) due to a variety of *Consul* agent definitions.
+Some DevOps engineers prefer to group services as *Consul* `catalog nodes`_ by customizing the assignment of host names to node names,
+while others focus on defining agent services with pure IP addresses as hosts, which relates to the `954`_ bug dilemma.
+
+Since version `13.5.2`_, the building of service downstream host/port in PR `909`_ has been altered to favor the node name as the host over the agent service address IP.
+
+Version `23.3`_ saw the introduction of a customization feature that allows control over the service building process through the ``DefaultConsulServiceBuilder`` class.
+This class has virtual methods that can be overridden to meet the needs of developers and DevOps.
+
+The present logic in the ``DefaultConsulServiceBuilder`` class is as follows:
+
+.. code-block:: csharp
+
+ protected virtual string GetDownstreamHost(ServiceEntry entry, Node node)
+ => node != null ? node.Name : entry.Service.Address;
+
+Some DevOps engineers choose to ignore node names, opting instead for abstract identifiers rather than actual hostnames.
+Our team, however, advocates for the assignment of real hostnames or IP addresses to node names, upholding this as a best practice.
+If this approach does not align with your needs, or if you prefer not to spend time detailing your nodes for downstream services, you might consider defining agent services without node names.
+In such cases within a *Consul* setup, you would need to override the behavior of the ``DefaultConsulServiceBuilder`` class.
+For further details, refer to the subsequent section below.
+
+.. _sd-addconsul-generic-method:
+
+``AddConsul`` method
+"""""""""""""""""""""""
+
+ | **Signature**: ``IOcelotBuilder AddConsul(this IOcelotBuilder builder)``
+
+Overriding the ``DefaultConsulServiceBuilder`` behavior involves two steps: defining a new class that inherits from the ``IConsulServiceBuilder`` interface,
+and then injecting this new behavior into DI using the ``AddConsul`` helper.
+However, the quickest and most streamlined approach is to inherit directly from the ``DefaultConsulServiceBuilder`` class, which offers greater flexibility.
+
+**First**, we need to define a new service building class:
+
+.. code-block:: csharp
+
+ public class MyConsulServiceBuilder : DefaultConsulServiceBuilder
+ {
+ public MyConsulServiceBuilder(Func configurationFactory, IConsulClientFactory clientFactory, IOcelotLoggerFactory loggerFactory)
+ : base(configurationFactory, clientFactory, loggerFactory) { }
+ // I want to use the agent service IP address as the downstream hostname
+ protected override string GetDownstreamHost(ServiceEntry entry, Node node) => entry.Service.Address;
+ }
+
+**Second**, we must inject the new behavior into DI, as demonstrated in the Ocelot versus Consul setup:
+
+.. code-block:: csharp
+
+ services.AddOcelot()
+ .AddConsul();
+
+You can refer to `the acceptance test`_ in the repository for an example.
+
Eureka
------
@@ -410,3 +531,22 @@ After this, you need to add the ``IServiceDiscoveryProviderFactory`` interface t
Note that in this case the Ocelot pipeline will not use ``ServiceDiscoveryProviderFactory`` by default.
Additionally, you do not need to specify ``"Type": "MyServiceDiscoveryProvider"`` in the **ServiceDiscoveryProvider** properties of the **GlobalConfiguration** settings.
But you can leave this ``Type`` option for compatibility between both designs.
+
+""""
+
+.. [#f1] :ref:`di-the-addocelot-method` adds default ASP.NET services to DI container. You could call another extended :ref:`di-addocelotusingbuilder-method` while configuring services to develop your own :ref:`di-custom-builder`. See more instructions in the ":ref:`di-addocelotusingbuilder-method`" section of :doc:`../features/dependencyinjection` feature.
+.. [#f2] *"Consul Configuration Key"* feature was requested in issue `346`_ as a part of version `7.0.0`_.
+.. [#f3] Customization of *"Consul Service Builder"* was implemented as a part of bug `954`_ fixing and the feature was delivered in version `23.3`_.
+
+.. _ocelot.json: https://github.com/ThreeMammals/Ocelot/blob/main/test/Ocelot.ManualTest/ocelot.json
+.. _Consul: https://www.consul.io/
+.. _KV Store: https://developer.hashicorp.com/consul/docs/dynamic-app-config/kv
+.. _3 seconds TTL: https://github.com/search?q=repo%3AThreeMammals%2FOcelot+TimeSpan.FromSeconds%283%29&type=code
+.. _catalog nodes: https://developer.hashicorp.com/consul/api-docs/catalog#list-nodes
+.. _the acceptance test: https://github.com/search?q=repo%3AThreeMammals%2FOcelot+Should_return_service_address_by_overridden_service_builder_when_there_is_a_node&type=code
+.. _346: https://github.com/ThreeMammals/Ocelot/issues/346
+.. _909: https://github.com/ThreeMammals/Ocelot/pull/909
+.. _954: https://github.com/ThreeMammals/Ocelot/issues/954
+.. _7.0.0: https://github.com/ThreeMammals/Ocelot/releases/tag/7.0.0
+.. _13.5.2: https://github.com/ThreeMammals/Ocelot/releases/tag/13.5.2
+.. _23.3: https://github.com/ThreeMammals/Ocelot/releases/tag/23.3.0
diff --git a/docs/index.rst b/docs/index.rst
index be0b6deb4..d208ae6d3 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,13 +1,49 @@
.. _Polly: https://github.com/App-vNext/Polly
-.. _@ebjornset: https://github.com/ebjornset
-.. _@RaynaldM: https://github.com/RaynaldM
-.. _@ArwynFr: https://github.com/ArwynFr
-.. _@AlyHKafoury: https://github.com/AlyHKafoury
-.. _@FelixBoers: https://github.com/FelixBoers
-.. _23.2: https://github.com/ThreeMammals/Ocelot/releases/tag/23.2.0
+.. _Circuit Breaker: https://www.pollydocs.org/strategies/circuit-breaker.html
+.. _Timeout: https://www.pollydocs.org/strategies/timeout.html
-Welcome to Ocelot `23.2`_
-======================================================================================
+.. _@raman-m: https://github.com/raman-m
+.. _@RaynaldM: https://github.com/RaynaldM
+.. _@jlukawska: https://github.com/jlukawska
+.. _@ibnuda: https://github.com/ibnuda
+.. _@vantm: https://github.com/vantm
+.. _@sergio-str: https://github.com/sergio-str
+.. _@PaulARoy: https://github.com/PaulARoy
+.. _@thiagoloureiro: https://github.com/thiagoloureiro
+.. _@bbenameur: https://github.com/bbenameur
+
+.. _23.3: https://github.com/ThreeMammals/Ocelot/releases/tag/23.3.0
+.. _23.3.0: https://github.com/ThreeMammals/Ocelot/releases/tag/23.3.0
+.. _23.2.0: https://github.com/ThreeMammals/Ocelot/releases/tag/23.2.0
+
+.. _954: https://github.com/ThreeMammals/Ocelot/issues/954
+.. _957: https://github.com/ThreeMammals/Ocelot/issues/957
+.. _1026: https://github.com/ThreeMammals/Ocelot/issues/1026
+.. _1312: https://github.com/ThreeMammals/Ocelot/pull/1312
+.. _1590: https://github.com/ThreeMammals/Ocelot/issues/1590
+.. _1592: https://github.com/ThreeMammals/Ocelot/pull/1592
+.. _1673: https://github.com/ThreeMammals/Ocelot/pull/1673
+.. _1843: https://github.com/ThreeMammals/Ocelot/pull/1843
+.. _2002: https://github.com/ThreeMammals/Ocelot/issues/2002
+.. _2003: https://github.com/ThreeMammals/Ocelot/pull/2003
+.. _2034: https://github.com/ThreeMammals/Ocelot/issues/2034
+.. _2039: https://github.com/ThreeMammals/Ocelot/issues/2039
+.. _2045: https://github.com/ThreeMammals/Ocelot/pull/2045
+.. _2050: https://github.com/ThreeMammals/Ocelot/pull/2050
+.. _2052: https://github.com/ThreeMammals/Ocelot/pull/2052
+.. _2054: https://github.com/ThreeMammals/Ocelot/discussions/2054
+.. _2058: https://github.com/ThreeMammals/Ocelot/pull/2058
+.. _2059: https://github.com/ThreeMammals/Ocelot/issues/2059
+.. _2067: https://github.com/ThreeMammals/Ocelot/pull/2067
+.. _2079: https://github.com/ThreeMammals/Ocelot/pull/2079
+.. _2085: https://github.com/ThreeMammals/Ocelot/issues/2085
+.. _2086: https://github.com/ThreeMammals/Ocelot/pull/2086
+
+.. role:: htm(raw)
+ :format: html
+
+Welcome to Ocelot `23.3`_
+=========================
Thanks for taking a look at the Ocelot documentation! Please use the left hand navigation to get around.
The team would suggest taking a look at the **Introduction** chapter first.
@@ -20,67 +56,108 @@ We **do** follow development process which is described in :doc:`../building/rel
Release Notes
-------------
- | **Release Tag**: `23.2.0 `_
- | **Release Codename**: `Lunar Eclipse `_
+| Release Tag: `23.3.0`_
+| Release Codename: **Twilight Texas**
+ :htm:`→` `for men `_
+ :htm:`→` `for women `_
+ :htm:`→` `for black men `_
What's new?
^^^^^^^^^^^
-- :doc:`../features/configuration`: A brand new :ref:`config-merging-tomemory` by `@ebjornset`_ as a part of the :ref:`config-merging-files` feature.
-
- The ``AddOcelot`` method merges the **ocelot.*.json** files into a single **ocelot.json** file as the primary configuration file, which is written back to disk and then added to the ``IConfigurationBuilder`` for the well-known ``IConfiguration``. You can now call another ``AddOcelot`` method that adds the merged JSON directly from memory to the ``IConfigurationBuilder``, using ``AddJsonStream`` instead.
-
- See more details in :ref:`di-configuration-overview` of :doc:`../features/dependencyinjection`.
+- :doc:`../features/servicediscovery`: Introducing a new feature for "*Customization of services creation*" in two primary service discovery providers: ``Consul`` (:ref:`sd-consul-service-builder`) and ``Kubernetes`` (:ref:`k8s-downstream-scheme-vs-port-names`), developed by `@raman-m`_.
+
+ The customization for both ``Consul`` and ``Kube`` providers in service creation is achieved through the overriding of virtual methods in default implementations. The recommendation was to separate the provider's logic and introduce ``public virtual`` and ``protected virtual`` methods in concrete classes, enabling:
+
+ - The use of ``public virtual`` methods as dictated by interface definitions.
+ - The application of ``protected virtual`` methods to allow developers to customize atomic operations through inheritance from existing concrete classes.
+ - The injection of new interface objects into the provider's constructor.
+ - The overriding of the default behavior of classes.
+
+ | Ultimately, customization relies on the virtual methods within the default implementation classes, providing developers the flexibility to override them as necessary for highly tailored Consul/K8s configurations in their specific environments.
+ | For further details, refer to the respective pull requests for both providers:
+
+ - ``Kube`` :htm:`→` PR `2052`_
+ - ``Consul`` :htm:`→` PR `2067`_
+
+- :doc:`../features/routing`: Introducing the new ":ref:`routing-upstream-headers`" feature by `@jlukawska`_.
+
+ | In addition to routing via ``UpstreamPathTemplate``, you can now define an ``UpstreamHeaderTemplates`` options dictionary. For a route to match, all headers specified in this section are required to be present in the request headers.
+ | For more details, see PR `1312`_.
+
+- :doc:`../features/configuration`: Introducing the ":ref:`config-version-policy`" feature by `@ibnuda`_.
+
+ The configurable ``HttpRequestMessage.VersionPolicy`` helps avoid HTTP protocol connection errors and stabilizes connections to downstream services, especially when you're not developing those services, documentation is scarce, or the deployed HTTP protocol version is uncertain.
+ For developers of downstream services, it's possible to ``ConfigureKestrel`` server and its endpoints with new protocol settings. However, attention to version policy is also required, and this feature provides precise version settings for HTTP connections.
-- :doc:`../features/servicefabric`: Published old undocumented :ref:`sf-placeholders` feature of :doc:`../features/servicefabric` `service discovery provider `_.
+ | Essentially, this feature promotes the use of HTTP protocols beyond 1.0/1.1, such as HTTP/2 or even HTTP/3.
+ | For additional details, refer to PR `1673`_.
- This feature by `@FelixBoers`_ is available starting from version `13.0.0 `_.
+- :doc:`../features/configuration`: Introducing the new ":ref:`config-route-metadata`" feature by `@vantm`_.
-- :doc:`../features/qualityofservice`: A brand new `Polly`_ v8 pipelines :ref:`qos-extensibility` feature by `@RaynaldM`_
+ Undoubtedly, this is the standout feature of the release! ⭐
+
+ Route metadata enables Ocelot developers to incorporate custom functions that address specific needs or to create their own plugins/extensions.
+
+ In versions of Ocelot prior to `23.3.0`_, the configuration was limited to predefined values that Ocelot used internally. This was sufficient for official extensions, but posed challenges for third-party developers who needed to implement configurations not included in the standard ``FileConfiguration``.
+ Applying an option to a specific route required knowledge of the array index and other details that might not be readily accessible using the standard ``IConfiguration`` or ``IOptions`` models from ASP.NET.
+
+ | Now, :doc:`../features/metadata` can be directly accessed in the ``DownstreamRoute`` object. Furthermore, metadata can also be retrieved from the global JSON section via the ``FileConfiguration.GlobalConfiguration`` property.
+ | For more information, see the details in PR `1843`_ on this remarkable feature.
Focus On
^^^^^^^^
Updates of the features
"""""""""""""""""""""""
-
- - :doc:`../features/configuration`: New :ref:`config-merging-tomemory` feature by `@ebjornset`_
- - :doc:`../features/dependencyinjection`: Added new overloaded :ref:`di-configuration-addocelot` by `@ebjornset`_
- - :doc:`../features/qualityofservice`: Support of new `Polly`_ v8 syntax and new :ref:`qos-extensibility` feature by `@RaynaldM`_
+
+- :doc:`../features/configuration`: New features are ":ref:`config-version-policy`" by `@ibnuda`_ and ":ref:`config-route-metadata`" by `@vantm`_.
+- :doc:`../features/servicediscovery`: New feature is "*Customization of services creation*" aka :ref:`sd-consul-service-builder` and :ref:`k8s-downstream-scheme-vs-port-names` by `@raman-m`_.
+- :doc:`../features/routing`: New feature is ":ref:`routing-upstream-headers`" by `@jlukawska`_.
+- :doc:`../features/qualityofservice`: The team has decided to remove the Polly V7 policies logic and the corresponding Ocelot ``AddPollyV7`` extensions (referenced in PR `2079`_).
+
+ | Furthermore, the Polly V8 Circuit Breaker has been mandated as the primary strategy (as per PR `2086`_).
+ | See more detaild below in "**Ocelot extra packages**" paragraph.
Ocelot extra packages
"""""""""""""""""""""
- - `Ocelot.Provider.Polly `_: Support of new `Polly`_ v8 syntax.
+- `Ocelot.Provider.Polly `_
- | *Polly* `8.0+ `_ versions introduced the concept of `resilience pipelines `_.
- | All `AddPolly extensions `_ have been automatically migrated from **v7** to **v8**.
- | Please note that older **v7** extensions are marked with the ``[Obsolete]`` attribute and renamed using the ``V7`` suffix. And the old **v7** implementation has been moved to the `v7 namespace `_.
- | See more details in :ref:`qos-polly-v7-vs-v8` section of :doc:`../features/qualityofservice` chapter.
+ - Our team has resolved to eliminate the Polly V7 policies logic and the corresponding Ocelot ``AddPollyV7`` extensions entirely (refer to the "`Polly v7 vs v8 `_" documentation).
+ In the previous `23.2.0`_ release, named `Lunar Eclipse `_, we included these to maintain the legacy `Polly`_ behavior, allowing development teams to transition or retain the old Polly V7 functionality.
+ We are now confident that it is time to progress alongside `Polly`_, shifting our focus to the new `Polly V8 `_ `resilience pipelines `_.
+ For more details, see PR `2079`_.
+ - Additionally, we have implemented Polly v8 `Circuit Breaker `_ as the primary strategy.
+ Our :doc:`../features/qualityofservice` (QoS) relies on two main strategies: :ref:`qos-circuit-breaker-strategy` and :ref:`qos-timeout-strategy`.
+ If both `Circuit Breaker`_ and `Timeout`_ have :ref:`qos-configuration` with their respective properties in the ``QoSOptions`` of the route JSON, then the :ref:`qos-circuit-breaker-strategy` will take precedence in the constructed resilience pipeline.
+ For more details, refer to PR `2086`_.
Stabilization aka bug fixing
""""""""""""""""""""""""""""
- - `683 `_ by PR `1927 `_. Thanks to `@AlyHKafoury`_!
+- Fixed `2034`_ in PR `2045`_ by `@raman-m`_
+- Fixed `2039`_ in PR `2050`_ by `@PaulARoy`_
+- Fixed `1590`_ in PR `1592`_ by `@sergio-str`_
+- Fixed `2054`_ `2059`_ in PR `2058`_ by `@thiagoloureiro`_
+- Fixed `954`_ `957`_ `1026`_ in PR `2067`_ by `@raman-m`_
+- Fixed `2002`_ in PR `2003`_ by `@bbenameur`_
+- Fixed `2085`_ in PR `2086`_ by `@RaynaldM`_
- | `New rules `_ have been added to Ocelot's configuration validation logic to find duplicate placeholders in path templates.
- | See more in the `FileConfigurationFluentValidator `_ class.
+See `all bugs `_ of the `Spring'24 `_ milestone
- - `1518 `_ hotfix by PR `1986 `_. Thanks to `@ArwynFr`_!
-
- | Using the default ``IServiceCollection`` `DI extensions `_ to register Ocelot services resulted in the ``ServiceCollection`` provider being forced to be created by calling ``BuildServiceProvider()``.
- | This resulted in problems with dependency injection libraries, or worse, causing the Ocelot app to crash!
- | See more in the `ServiceCollectionExtensions `_ class.
-
- - See `all bugs `_ of the `February'24 `_ milestone
-
-Updated Documentation
-"""""""""""""""""""""
+Documentation for version `23.3`_
+"""""""""""""""""""""""""""""""""
- - :doc:`../features/configuration`
- - :doc:`../features/dependencyinjection`
- - :doc:`../features/qualityofservice`
- - :doc:`../features/servicefabric`
+- :doc:`../features/caching`: New :ref:`cch-enablecontenthashing-option` and :ref:`cch-global-configuration` sections
+- :doc:`../features/configuration`: New :ref:`config-version-policy` and :ref:`config-route-metadata` sections
+- :doc:`../features/kubernetes`: New :ref:`k8s-downstream-scheme-vs-port-names` section
+- :doc:`../features/metadata`: This is new chapter for :ref:`config-route-metadata` feature
+- :doc:`../features/qualityofservice`
+- :doc:`../features/ratelimiting`
+- :doc:`../features/requestaggregation`
+- :doc:`../features/routing`: New :ref:`routing-upstream-headers` section
+- :doc:`../features/servicediscovery`: New :ref:`sd-consul-service-builder` and :ref:`k8s-downstream-scheme-vs-port-names` sections
.. toctree::
@@ -113,6 +190,7 @@ Updated Documentation
features/kubernetes
features/loadbalancer
features/logging
+ features/metadata
features/methodtransformation
features/middlewareinjection
features/qualityofservice
diff --git a/docs/make.bat b/docs/make.bat
old mode 100755
new mode 100644
diff --git a/samples/AdministrationApi/Issue645.postman_collection.json b/samples/Administration/Issue645.postman_collection.json
similarity index 97%
rename from samples/AdministrationApi/Issue645.postman_collection.json
rename to samples/Administration/Issue645.postman_collection.json
index c9bac89f5..6fedd16b9 100644
--- a/samples/AdministrationApi/Issue645.postman_collection.json
+++ b/samples/Administration/Issue645.postman_collection.json
@@ -1,150 +1,150 @@
-{
- "info": {
- "_postman_id": "6234b40a-e363-4c73-8577-1c9074abb951",
- "name": "Issue645",
- "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
- },
- "item": [
- {
- "name": "1. GET http://localhost: 55580/administration/.well-known/openid-configuration",
- "request": {
- "method": "GET",
- "header": [
- {
- "key": "Authorization",
- "value": "Bearer {{AccessToken}}"
- }
- ],
- "body": {},
- "url": {
- "raw": "http://localhost:5000/administration/.well-known/openid-configuration",
- "protocol": "http",
- "host": [
- "localhost"
- ],
- "port": "5000",
- "path": [
- "administration",
- ".well-known",
- "openid-configuration"
- ]
- }
- },
- "response": []
- },
- {
- "name": "3. GET http://localhost: 55580/administration/configuration",
- "request": {
- "method": "POST",
- "header": [
- {
- "key": "Authorization",
- "value": "Bearer {{AccessToken}}"
- },
- {
- "key": "Content-Type",
- "value": "application/json"
- }
- ],
- "body": {
- "mode": "raw",
- "raw": "{\r\n \"routes\": [\r\n {\r\n \"downstreamPathTemplate\": \"/{everything}\",\r\n \"upstreamPathTemplate\": \"/templates/{everything}\",\r\n \"upstreamHttpMethod\": [\r\n \"GET\"\r\n ],\r\n \"addHeadersToRequest\": {},\r\n \"upstreamHeaderTransform\": {},\r\n \"downstreamHeaderTransform\": {},\r\n \"addClaimsToRequest\": {},\r\n \"routeClaimsRequirement\": {},\r\n \"addQueriesToRequest\": {},\r\n \"requestIdKey\": null,\r\n \"fileCacheOptions\": {\r\n \"ttlSeconds\": 0,\r\n \"region\": null\r\n },\r\n \"routeIsCaseSensitive\": false,\r\n \"downstreamScheme\": \"http\",\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"rateLimitOptions\": {\r\n \"clientWhitelist\": [],\r\n \"enableRateLimiting\": false,\r\n \"period\": null,\r\n \"periodTimespan\": 0,\r\n \"limit\": 0\r\n },\r\n \"authenticationOptions\": {\r\n \"authenticationProviderKey\": null,\r\n \"allowedScopes\": []\r\n },\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n },\r\n \"downstreamHostAndPorts\": [\r\n {\r\n \"host\": \"localhost\",\r\n \"port\": 50689\r\n }\r\n ],\r\n \"upstreamHost\": null,\r\n \"key\": null,\r\n \"delegatingHandlers\": [],\r\n \"priority\": 1,\r\n \"timeout\": 0,\r\n \"dangerousAcceptAnyServerCertificateValidator\": false\r\n }\r\n ],\r\n \"aggregates\": [],\r\n \"globalConfiguration\": {\r\n \"requestIdKey\": \"Request-Id\",\r\n \"rateLimitOptions\": {\r\n \"clientIdHeader\": \"ClientId\",\r\n \"quotaExceededMessage\": null,\r\n \"rateLimitCounterPrefix\": \"ocelot\",\r\n \"disableRateLimitHeaders\": false,\r\n \"httpStatusCode\": 429\r\n },\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"baseUrl\": \"http://localhost:55580\",\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"downstreamScheme\": null,\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n }\r\n }\r\n}"
- },
- "url": {
- "raw": "http://localhost:5000/administration/configuration",
- "protocol": "http",
- "host": [
- "localhost"
- ],
- "port": "5000",
- "path": [
- "administration",
- "configuration"
- ]
- }
- },
- "response": []
- },
- {
- "name": "2. POST http://localhost: 55580/administration/connect/token",
- "event": [
- {
- "listen": "test",
- "script": {
- "type": "text/javascript",
- "exec": [
- "var jsonData = JSON.parse(responseBody);",
- "postman.setGlobalVariable(\"AccessToken\", jsonData.access_token);",
- "postman.setGlobalVariable(\"RefreshToken\", jsonData.refresh_token);"
- ]
- }
- }
- ],
- "request": {
- "method": "POST",
- "header": [],
- "body": {
- "mode": "formdata",
- "formdata": [
- {
- "key": "client_id",
- "value": "admin",
- "type": "text"
- },
- {
- "key": "client_secret",
- "value": "secret",
- "type": "text"
- },
- {
- "key": "scope",
- "value": "admin",
- "type": "text"
- },
- {
- "key": "grant_type",
- "value": "client_credentials",
- "type": "text"
- }
- ]
- },
- "url": {
- "raw": "http://localhost:5000/administration/connect/token",
- "protocol": "http",
- "host": [
- "localhost"
- ],
- "port": "5000",
- "path": [
- "administration",
- "connect",
- "token"
- ]
- }
- },
- "response": []
- }
- ],
- "event": [
- {
- "listen": "prerequest",
- "script": {
- "id": "0f60e7b3-e4f1-4458-bbc4-fc4809e86b2d",
- "type": "text/javascript",
- "exec": [
- string.Empty
- ]
- }
- },
- {
- "listen": "test",
- "script": {
- "id": "1279a2cf-b771-4a86-9dfa-302b240fac62",
- "type": "text/javascript",
- "exec": [
- string.Empty
- ]
- }
- }
- ]
+{
+ "info": {
+ "_postman_id": "6234b40a-e363-4c73-8577-1c9074abb951",
+ "name": "Issue645",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
+ },
+ "item": [
+ {
+ "name": "1. GET http://localhost: 55580/administration/.well-known/openid-configuration",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{AccessToken}}"
+ }
+ ],
+ "body": {},
+ "url": {
+ "raw": "http://localhost:5000/administration/.well-known/openid-configuration",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "administration",
+ ".well-known",
+ "openid-configuration"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "3. GET http://localhost: 55580/administration/configuration",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Authorization",
+ "value": "Bearer {{AccessToken}}"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"routes\": [\r\n {\r\n \"downstreamPathTemplate\": \"/{everything}\",\r\n \"upstreamPathTemplate\": \"/templates/{everything}\",\r\n \"upstreamHttpMethod\": [\r\n \"GET\"\r\n ],\r\n \"addHeadersToRequest\": {},\r\n \"upstreamHeaderTransform\": {},\r\n \"downstreamHeaderTransform\": {},\r\n \"addClaimsToRequest\": {},\r\n \"routeClaimsRequirement\": {},\r\n \"addQueriesToRequest\": {},\r\n \"requestIdKey\": null,\r\n \"fileCacheOptions\": {\r\n \"ttlSeconds\": 0,\r\n \"region\": null\r\n },\r\n \"routeIsCaseSensitive\": false,\r\n \"downstreamScheme\": \"http\",\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"rateLimitOptions\": {\r\n \"clientWhitelist\": [],\r\n \"enableRateLimiting\": false,\r\n \"period\": null,\r\n \"periodTimespan\": 0,\r\n \"limit\": 0\r\n },\r\n \"authenticationOptions\": {\r\n \"authenticationProviderKey\": null,\r\n \"allowedScopes\": []\r\n },\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n },\r\n \"downstreamHostAndPorts\": [\r\n {\r\n \"host\": \"localhost\",\r\n \"port\": 50689\r\n }\r\n ],\r\n \"upstreamHost\": null,\r\n \"key\": null,\r\n \"delegatingHandlers\": [],\r\n \"priority\": 1,\r\n \"timeout\": 0,\r\n \"dangerousAcceptAnyServerCertificateValidator\": false\r\n }\r\n ],\r\n \"aggregates\": [],\r\n \"globalConfiguration\": {\r\n \"requestIdKey\": \"Request-Id\",\r\n \"rateLimitOptions\": {\r\n \"clientIdHeader\": \"ClientId\",\r\n \"quotaExceededMessage\": null,\r\n \"rateLimitCounterPrefix\": \"ocelot\",\r\n \"disableRateLimitHeaders\": false,\r\n \"httpStatusCode\": 429\r\n },\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"baseUrl\": \"http://localhost:55580\",\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"downstreamScheme\": null,\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n }\r\n }\r\n}"
+ },
+ "url": {
+ "raw": "http://localhost:5000/administration/configuration",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "administration",
+ "configuration"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "2. POST http://localhost: 55580/administration/connect/token",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ "var jsonData = JSON.parse(responseBody);",
+ "postman.setGlobalVariable(\"AccessToken\", jsonData.access_token);",
+ "postman.setGlobalVariable(\"RefreshToken\", jsonData.refresh_token);"
+ ]
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "client_id",
+ "value": "admin",
+ "type": "text"
+ },
+ {
+ "key": "client_secret",
+ "value": "secret",
+ "type": "text"
+ },
+ {
+ "key": "scope",
+ "value": "admin",
+ "type": "text"
+ },
+ {
+ "key": "grant_type",
+ "value": "client_credentials",
+ "type": "text"
+ }
+ ]
+ },
+ "url": {
+ "raw": "http://localhost:5000/administration/connect/token",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "administration",
+ "connect",
+ "token"
+ ]
+ }
+ },
+ "response": []
+ }
+ ],
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "id": "0f60e7b3-e4f1-4458-bbc4-fc4809e86b2d",
+ "type": "text/javascript",
+ "exec": [
+ string.Empty
+ ]
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "id": "1279a2cf-b771-4a86-9dfa-302b240fac62",
+ "type": "text/javascript",
+ "exec": [
+ string.Empty
+ ]
+ }
+ }
+ ]
}
\ No newline at end of file
diff --git a/samples/AdministrationApi/AdministrationApi.csproj b/samples/Administration/Ocelot.Samples.AdministrationApi.csproj
similarity index 100%
rename from samples/AdministrationApi/AdministrationApi.csproj
rename to samples/Administration/Ocelot.Samples.AdministrationApi.csproj
diff --git a/samples/AdministrationApi/Program.cs b/samples/Administration/Program.cs
similarity index 100%
rename from samples/AdministrationApi/Program.cs
rename to samples/Administration/Program.cs
diff --git a/samples/AdministrationApi/Properties/launchSettings.json b/samples/Administration/Properties/launchSettings.json
similarity index 100%
rename from samples/AdministrationApi/Properties/launchSettings.json
rename to samples/Administration/Properties/launchSettings.json
diff --git a/samples/AdministrationApi/README.md b/samples/Administration/README.md
similarity index 96%
rename from samples/AdministrationApi/README.md
rename to samples/Administration/README.md
index 42a01b2f4..59d236aa7 100644
--- a/samples/AdministrationApi/README.md
+++ b/samples/Administration/README.md
@@ -1,94 +1,94 @@
-```json
-{
- "routes": [
- {
- "downstreamPathTemplate": "/{everything}",
- "upstreamPathTemplate": "/templates/{everything}",
- "upstreamHttpMethod": [
- "GET"
- ],
- "addHeadersToRequest": {},
- "upstreamHeaderTransform": {},
- "downstreamHeaderTransform": {},
- "addClaimsToRequest": {},
- "routeClaimsRequirement": {},
- "addQueriesToRequest": {},
- "requestIdKey": null,
- "fileCacheOptions": {
- "ttlSeconds": 0,
- "region": null
- },
- "routeIsCaseSensitive": false,
- "downstreamScheme": "http",
- "qoSOptions": {
- "exceptionsAllowedBeforeBreaking": 0,
- "durationOfBreak": 0,
- "timeoutValue": 0
- },
- "loadBalancerOptions": {
- "type": null,
- "key": null,
- "expiry": 0
- },
- "rateLimitOptions": {
- "clientWhitelist": [],
- "enableRateLimiting": false,
- "period": null,
- "periodTimespan": 0,
- "limit": 0
- },
- "authenticationOptions": {
- "authenticationProviderKey": null,
- "allowedScopes": []
- },
- "httpHandlerOptions": {
- "allowAutoRedirect": false,
- "useCookieContainer": false,
- "useTracing": false,
- "useProxy": true
- },
- "downstreamHostAndPorts": [
- {
- "host": "localhost",
- "port": 50689
- }
- ],
- "upstreamHost": null,
- "key": null,
- "delegatingHandlers": [],
- "priority": 1,
- "timeout": 0,
- "dangerousAcceptAnyServerCertificateValidator": false
- }
- ],
- "aggregates": [],
- "globalConfiguration": {
- "requestIdKey": "Request-Id",
- "rateLimitOptions": {
- "clientIdHeader": "ClientId",
- "quotaExceededMessage": null,
- "rateLimitCounterPrefix": "ocelot",
- "disableRateLimitHeaders": false,
- "httpStatusCode": 429
- },
- "qoSOptions": {
- "exceptionsAllowedBeforeBreaking": 0,
- "durationOfBreak": 0,
- "timeoutValue": 0
- },
- "baseUrl": "http://localhost:55580",
- "loadBalancerOptions": {
- "type": null,
- "key": null,
- "expiry": 0
- },
- "downstreamScheme": null,
- "httpHandlerOptions": {
- "allowAutoRedirect": false,
- "useCookieContainer": false,
- "useTracing": false,
- "useProxy": true
- }
- }
-}
-```
+```json
+{
+ "routes": [
+ {
+ "downstreamPathTemplate": "/{everything}",
+ "upstreamPathTemplate": "/templates/{everything}",
+ "upstreamHttpMethod": [
+ "GET"
+ ],
+ "addHeadersToRequest": {},
+ "upstreamHeaderTransform": {},
+ "downstreamHeaderTransform": {},
+ "addClaimsToRequest": {},
+ "routeClaimsRequirement": {},
+ "addQueriesToRequest": {},
+ "requestIdKey": null,
+ "fileCacheOptions": {
+ "ttlSeconds": 0,
+ "region": null
+ },
+ "routeIsCaseSensitive": false,
+ "downstreamScheme": "http",
+ "qoSOptions": {
+ "exceptionsAllowedBeforeBreaking": 0,
+ "durationOfBreak": 0,
+ "timeoutValue": 0
+ },
+ "loadBalancerOptions": {
+ "type": null,
+ "key": null,
+ "expiry": 0
+ },
+ "rateLimitOptions": {
+ "clientWhitelist": [],
+ "enableRateLimiting": false,
+ "period": null,
+ "periodTimespan": 0,
+ "limit": 0
+ },
+ "authenticationOptions": {
+ "authenticationProviderKey": null,
+ "allowedScopes": []
+ },
+ "httpHandlerOptions": {
+ "allowAutoRedirect": false,
+ "useCookieContainer": false,
+ "useTracing": false,
+ "useProxy": true
+ },
+ "downstreamHostAndPorts": [
+ {
+ "host": "localhost",
+ "port": 50689
+ }
+ ],
+ "upstreamHost": null,
+ "key": null,
+ "delegatingHandlers": [],
+ "priority": 1,
+ "timeout": 0,
+ "dangerousAcceptAnyServerCertificateValidator": false
+ }
+ ],
+ "aggregates": [],
+ "globalConfiguration": {
+ "requestIdKey": "Request-Id",
+ "rateLimitOptions": {
+ "clientIdHeader": "ClientId",
+ "quotaExceededMessage": null,
+ "rateLimitCounterPrefix": "ocelot",
+ "disableRateLimitHeaders": false,
+ "httpStatusCode": 429
+ },
+ "qoSOptions": {
+ "exceptionsAllowedBeforeBreaking": 0,
+ "durationOfBreak": 0,
+ "timeoutValue": 0
+ },
+ "baseUrl": "http://localhost:55580",
+ "loadBalancerOptions": {
+ "type": null,
+ "key": null,
+ "expiry": 0
+ },
+ "downstreamScheme": null,
+ "httpHandlerOptions": {
+ "allowAutoRedirect": false,
+ "useCookieContainer": false,
+ "useTracing": false,
+ "useProxy": true
+ }
+ }
+}
+```
diff --git a/samples/AdministrationApi/appsettings.json b/samples/Administration/appsettings.json
similarity index 100%
rename from samples/AdministrationApi/appsettings.json
rename to samples/Administration/appsettings.json
diff --git a/samples/AdministrationApi/ocelot.json b/samples/Administration/ocelot.json
similarity index 95%
rename from samples/AdministrationApi/ocelot.json
rename to samples/Administration/ocelot.json
index 0fa4143ff..02e7c5512 100644
--- a/samples/AdministrationApi/ocelot.json
+++ b/samples/Administration/ocelot.json
@@ -1,18 +1,18 @@
-{
- "Routes": [
- {
- "DownstreamPathTemplate": "/service/stats/collected",
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "localhost",
- "Port": 5100
- }
- ],
- "UpstreamPathTemplate": "/api/stats/collected"
- }
- ],
- "GlobalConfiguration": {
- "BaseUrl": "http://localhost:5000"
- }
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/service/stats/collected",
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "localhost",
+ "Port": 5100
+ }
+ ],
+ "UpstreamPathTemplate": "/api/stats/collected"
+ }
+ ],
+ "GlobalConfiguration": {
+ "BaseUrl": "http://localhost:5000"
+ }
}
\ No newline at end of file
diff --git a/samples/AdministrationApi/tempkey.rsa b/samples/Administration/tempkey.rsa
similarity index 100%
rename from samples/AdministrationApi/tempkey.rsa
rename to samples/Administration/tempkey.rsa
diff --git a/samples/OcelotBasic/Ocelot.Samples.OcelotBasic.ApiGateway.csproj b/samples/Basic/Ocelot.Samples.Basic.ApiGateway.csproj
similarity index 100%
rename from samples/OcelotBasic/Ocelot.Samples.OcelotBasic.ApiGateway.csproj
rename to samples/Basic/Ocelot.Samples.Basic.ApiGateway.csproj
diff --git a/samples/OcelotBasic/Program.cs b/samples/Basic/Program.cs
similarity index 97%
rename from samples/OcelotBasic/Program.cs
rename to samples/Basic/Program.cs
index 34c555f19..3901407c2 100644
--- a/samples/OcelotBasic/Program.cs
+++ b/samples/Basic/Program.cs
@@ -1,29 +1,29 @@
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
-using System.IO;
+using System.IO;
-namespace Ocelot.Samples.OcelotBasic.ApiGateway;
-
-public class Program
-{
- public static void Main(string[] args)
- {
- new WebHostBuilder()
- .UseKestrel()
- .UseContentRoot(Directory.GetCurrentDirectory())
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config
- .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
- .AddJsonFile("appsettings.json", true, true)
- .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddJsonFile("ocelot.json")
- .AddEnvironmentVariables();
- })
- .ConfigureLogging((hostingContext, logging) =>
+namespace Ocelot.Samples.OcelotBasic.ApiGateway;
+
+public class Program
+{
+ public static void Main(string[] args)
+ {
+ new WebHostBuilder()
+ .UseKestrel()
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", true, true)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
+ .AddJsonFile("ocelot.json")
+ .AddEnvironmentVariables();
+ })
+ .ConfigureLogging((hostingContext, logging) =>
{
if (hostingContext.HostingEnvironment.IsDevelopment())
{
@@ -31,10 +31,10 @@ public static void Main(string[] args)
logging.AddConsole();
}
//add your logging
- })
- .UseIISIntegration()
+ })
+ .UseIISIntegration()
.UseStartup()
- .Build()
- .Run();
- }
-}
+ .Build()
+ .Run();
+ }
+}
diff --git a/samples/OcelotBasic/Properties/launchSettings.json b/samples/Basic/Properties/launchSettings.json
similarity index 100%
rename from samples/OcelotBasic/Properties/launchSettings.json
rename to samples/Basic/Properties/launchSettings.json
diff --git a/samples/OcelotBasic/Startup.cs b/samples/Basic/Startup.cs
similarity index 100%
rename from samples/OcelotBasic/Startup.cs
rename to samples/Basic/Startup.cs
diff --git a/samples/OcelotBasic/appsettings.Development.json b/samples/Basic/appsettings.Development.json
similarity index 100%
rename from samples/OcelotBasic/appsettings.Development.json
rename to samples/Basic/appsettings.Development.json
diff --git a/samples/OcelotKube/ApiGateway/appsettings.json b/samples/Basic/appsettings.json
similarity index 100%
rename from samples/OcelotKube/ApiGateway/appsettings.json
rename to samples/Basic/appsettings.json
diff --git a/samples/OcelotBasic/ocelot.json b/samples/Basic/ocelot.json
similarity index 95%
rename from samples/OcelotBasic/ocelot.json
rename to samples/Basic/ocelot.json
index 2864550cd..7cab02430 100644
--- a/samples/OcelotBasic/ocelot.json
+++ b/samples/Basic/ocelot.json
@@ -1,21 +1,21 @@
-{
- "Routes": [
- {
- "DownstreamPathTemplate": "/todos/{id}",
- "DownstreamScheme": "https",
- "DownstreamHostAndPorts": [
- {
- "Host": "jsonplaceholder.typicode.com",
- "Port": 443
- }
- ],
- "UpstreamPathTemplate": "/posts/{id}",
- "UpstreamHttpMethod": [
- "Get"
- ]
- }
- ],
- "GlobalConfiguration": {
- "BaseUrl": "https://localhost:5000"
- }
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/todos/{id}",
+ "DownstreamScheme": "https",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "jsonplaceholder.typicode.com",
+ "Port": 443
+ }
+ ],
+ "UpstreamPathTemplate": "/posts/{id}",
+ "UpstreamHttpMethod": [
+ "Get"
+ ]
+ }
+ ],
+ "GlobalConfiguration": {
+ "BaseUrl": "https://localhost:5000"
+ }
}
diff --git a/samples/Docker/README.md b/samples/Docker/README.md
deleted file mode 100644
index e69de29bb..000000000
diff --git a/samples/OcelotEureka/ApiGateway/ApiGateway.csproj b/samples/Eureka/ApiGateway/Ocelot.Samples.Eureka.ApiGateway.csproj
similarity index 100%
rename from samples/OcelotEureka/ApiGateway/ApiGateway.csproj
rename to samples/Eureka/ApiGateway/Ocelot.Samples.Eureka.ApiGateway.csproj
diff --git a/samples/OcelotEureka/ApiGateway/Program.cs b/samples/Eureka/ApiGateway/Program.cs
similarity index 100%
rename from samples/OcelotEureka/ApiGateway/Program.cs
rename to samples/Eureka/ApiGateway/Program.cs
diff --git a/samples/OcelotEureka/ApiGateway/Properties/launchSettings.json b/samples/Eureka/ApiGateway/Properties/launchSettings.json
similarity index 100%
rename from samples/OcelotEureka/ApiGateway/Properties/launchSettings.json
rename to samples/Eureka/ApiGateway/Properties/launchSettings.json
diff --git a/samples/OcelotEureka/ApiGateway/appsettings.json b/samples/Eureka/ApiGateway/appsettings.json
similarity index 100%
rename from samples/OcelotEureka/ApiGateway/appsettings.json
rename to samples/Eureka/ApiGateway/appsettings.json
diff --git a/samples/OcelotEureka/ApiGateway/ocelot.json b/samples/Eureka/ApiGateway/ocelot.json
similarity index 96%
rename from samples/OcelotEureka/ApiGateway/ocelot.json
rename to samples/Eureka/ApiGateway/ocelot.json
index 5a69973de..747cf23c8 100644
--- a/samples/OcelotEureka/ApiGateway/ocelot.json
+++ b/samples/Eureka/ApiGateway/ocelot.json
@@ -1,22 +1,22 @@
-{
- "Routes": [
- {
- "DownstreamPathTemplate": "/api/Category",
- "DownstreamScheme": "http",
- "UpstreamPathTemplate": "/Category",
- "ServiceName": "ncore-rat",
- "UpstreamHttpMethod": [ "Get" ],
- "QoSOptions": {
- "ExceptionsAllowedBeforeBreaking": 3,
- "DurationOfBreak": 10000,
- "TimeoutValue": 5000
- },
- "FileCacheOptions": { "TtlSeconds": 15 }
- }
- ],
- "GlobalConfiguration": {
- "RequestIdKey": "OcRequestId",
- "AdministrationPath": "/administration",
- "ServiceDiscoveryProvider": { "Type": "Eureka" }
- }
-}
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/api/Category",
+ "DownstreamScheme": "http",
+ "UpstreamPathTemplate": "/Category",
+ "ServiceName": "ncore-rat",
+ "UpstreamHttpMethod": [ "Get" ],
+ "QoSOptions": {
+ "ExceptionsAllowedBeforeBreaking": 3,
+ "DurationOfBreak": 10000,
+ "TimeoutValue": 5000
+ },
+ "FileCacheOptions": { "TtlSeconds": 15 }
+ }
+ ],
+ "GlobalConfiguration": {
+ "RequestIdKey": "OcRequestId",
+ "AdministrationPath": "/administration",
+ "ServiceDiscoveryProvider": { "Type": "Eureka" }
+ }
+}
diff --git a/samples/OcelotEureka/DownstreamService/Controllers/CategoryController.cs b/samples/Eureka/DownstreamService/Controllers/CategoryController.cs
similarity index 100%
rename from samples/OcelotEureka/DownstreamService/Controllers/CategoryController.cs
rename to samples/Eureka/DownstreamService/Controllers/CategoryController.cs
diff --git a/samples/OcelotEureka/DownstreamService/DownstreamService.csproj b/samples/Eureka/DownstreamService/Ocelot.Samples.Eureka.DownstreamService.csproj
similarity index 100%
rename from samples/OcelotEureka/DownstreamService/DownstreamService.csproj
rename to samples/Eureka/DownstreamService/Ocelot.Samples.Eureka.DownstreamService.csproj
diff --git a/samples/OcelotEureka/DownstreamService/Program.cs b/samples/Eureka/DownstreamService/Program.cs
similarity index 100%
rename from samples/OcelotEureka/DownstreamService/Program.cs
rename to samples/Eureka/DownstreamService/Program.cs
diff --git a/samples/OcelotEureka/DownstreamService/Properties/launchSettings.json b/samples/Eureka/DownstreamService/Properties/launchSettings.json
similarity index 100%
rename from samples/OcelotEureka/DownstreamService/Properties/launchSettings.json
rename to samples/Eureka/DownstreamService/Properties/launchSettings.json
diff --git a/samples/OcelotEureka/DownstreamService/Startup.cs b/samples/Eureka/DownstreamService/Startup.cs
similarity index 100%
rename from samples/OcelotEureka/DownstreamService/Startup.cs
rename to samples/Eureka/DownstreamService/Startup.cs
diff --git a/samples/OcelotEureka/DownstreamService/appsettings.Development.json b/samples/Eureka/DownstreamService/appsettings.Development.json
similarity index 100%
rename from samples/OcelotEureka/DownstreamService/appsettings.Development.json
rename to samples/Eureka/DownstreamService/appsettings.Development.json
diff --git a/samples/OcelotEureka/DownstreamService/appsettings.json b/samples/Eureka/DownstreamService/appsettings.json
similarity index 100%
rename from samples/OcelotEureka/DownstreamService/appsettings.json
rename to samples/Eureka/DownstreamService/appsettings.json
diff --git a/samples/OcelotEureka/OcelotEureka.sln b/samples/Eureka/OcelotEureka.sln
similarity index 100%
rename from samples/OcelotEureka/OcelotEureka.sln
rename to samples/Eureka/OcelotEureka.sln
diff --git a/samples/OcelotEureka/README.md b/samples/Eureka/README.md
similarity index 100%
rename from samples/OcelotEureka/README.md
rename to samples/Eureka/README.md
diff --git a/samples/OcelotGraphQL/OcelotGraphQL.csproj b/samples/GraphQL/Ocelot.Samples.GraphQL.csproj
similarity index 100%
rename from samples/OcelotGraphQL/OcelotGraphQL.csproj
rename to samples/GraphQL/Ocelot.Samples.GraphQL.csproj
diff --git a/samples/OcelotGraphQL/OcelotGraphQL.sln b/samples/GraphQL/OcelotGraphQL.sln
similarity index 100%
rename from samples/OcelotGraphQL/OcelotGraphQL.sln
rename to samples/GraphQL/OcelotGraphQL.sln
diff --git a/samples/OcelotGraphQL/Program.cs b/samples/GraphQL/Program.cs
similarity index 96%
rename from samples/OcelotGraphQL/Program.cs
rename to samples/GraphQL/Program.cs
index e519875f1..e2f19aaaf 100644
--- a/samples/OcelotGraphQL/Program.cs
+++ b/samples/GraphQL/Program.cs
@@ -1,134 +1,134 @@
-using GraphQL;
-using GraphQL.Types;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Ocelot.DependencyInjection;
-using Ocelot.Middleware;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace OcelotGraphQL
-{
- public class Hero
- {
- public int Id { get; set; }
- public string Name { get; set; }
- }
-
- public class Query
- {
- private readonly List _heroes = new()
- {
- new Hero { Id = 1, Name = "R2-D2" },
- new Hero { Id = 2, Name = "Batman" },
- new Hero { Id = 3, Name = "Wonder Woman" },
- new Hero { Id = 4, Name = "Tom Pallister" }
- };
-
- [GraphQLMetadata("hero")]
- public Hero GetHero(int id)
- {
- return _heroes.FirstOrDefault(x => x.Id == id);
- }
- }
-
- public class GraphQlDelegatingHandler : DelegatingHandler
- {
- //private readonly ISchema _schema;
- private readonly IDocumentExecuter _executer;
- private readonly IDocumentWriter _writer;
-
- public GraphQlDelegatingHandler(IDocumentExecuter executer, IDocumentWriter writer)
- {
- _executer = executer;
- _writer = writer;
- }
-
- protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- //try get query from body, could check http method :)
- var query = await request.Content.ReadAsStringAsync(cancellationToken);
-
- //if not body try query string, dont hack like this in real world..
- if (query.Length == 0)
- {
- var decoded = WebUtility.UrlDecode(request.RequestUri.Query);
- query = decoded.Replace("?query=", string.Empty);
- }
-
- var result = await _executer.ExecuteAsync(_ =>
- {
- _.Query = query;
- });
-
- var responseBody = await _writer.WriteToStringAsync(result);
-
- //maybe check for errors and headers etc in real world?
- var response = new HttpResponseMessage(HttpStatusCode.OK)
- {
- Content = new StringContent(responseBody)
- };
-
- //ocelot will treat this like any other http request...
- return response;
- }
- }
-
- public class Program
- {
- public static void Main()
- {
- var schema = Schema.For(@"
- type Hero {
- id: Int
- name: String
- }
-
- type Query {
- hero(id: Int): Hero
- }
- ", _ =>
- {
- _.Types.Include();
- });
-
- new WebHostBuilder()
- .UseKestrel()
- .UseContentRoot(Directory.GetCurrentDirectory())
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config
- .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
- .AddJsonFile("appsettings.json", true, true)
- .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
- .AddJsonFile("ocelot.json", false, false)
- .AddEnvironmentVariables();
- })
- .ConfigureServices(s =>
- {
- s.AddSingleton(schema);
- s.AddOcelot()
- .AddDelegatingHandler();
- })
- .ConfigureLogging((hostingContext, logging) =>
- {
- logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
- logging.AddConsole();
- })
- .UseIISIntegration()
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- })
- .Build()
- .Run();
- }
- }
-}
+using GraphQL;
+using GraphQL.Types;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Ocelot.DependencyInjection;
+using Ocelot.Middleware;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace OcelotGraphQL
+{
+ public class Hero
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ }
+
+ public class Query
+ {
+ private readonly List _heroes = new()
+ {
+ new Hero { Id = 1, Name = "R2-D2" },
+ new Hero { Id = 2, Name = "Batman" },
+ new Hero { Id = 3, Name = "Wonder Woman" },
+ new Hero { Id = 4, Name = "Tom Pallister" }
+ };
+
+ [GraphQLMetadata("hero")]
+ public Hero GetHero(int id)
+ {
+ return _heroes.FirstOrDefault(x => x.Id == id);
+ }
+ }
+
+ public class GraphQlDelegatingHandler : DelegatingHandler
+ {
+ //private readonly ISchema _schema;
+ private readonly IDocumentExecuter _executer;
+ private readonly IDocumentWriter _writer;
+
+ public GraphQlDelegatingHandler(IDocumentExecuter executer, IDocumentWriter writer)
+ {
+ _executer = executer;
+ _writer = writer;
+ }
+
+ protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ //try get query from body, could check http method :)
+ var query = await request.Content.ReadAsStringAsync(cancellationToken);
+
+ //if not body try query string, dont hack like this in real world..
+ if (query.Length == 0)
+ {
+ var decoded = WebUtility.UrlDecode(request.RequestUri.Query);
+ query = decoded.Replace("?query=", string.Empty);
+ }
+
+ var result = await _executer.ExecuteAsync(_ =>
+ {
+ _.Query = query;
+ });
+
+ var responseBody = await _writer.WriteToStringAsync(result);
+
+ //maybe check for errors and headers etc in real world?
+ var response = new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(responseBody)
+ };
+
+ //ocelot will treat this like any other http request...
+ return response;
+ }
+ }
+
+ public class Program
+ {
+ public static void Main()
+ {
+ var schema = Schema.For(@"
+ type Hero {
+ id: Int
+ name: String
+ }
+
+ type Query {
+ hero(id: Int): Hero
+ }
+ ", _ =>
+ {
+ _.Types.Include();
+ });
+
+ new WebHostBuilder()
+ .UseKestrel()
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", true, true)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
+ .AddJsonFile("ocelot.json", false, false)
+ .AddEnvironmentVariables();
+ })
+ .ConfigureServices(s =>
+ {
+ s.AddSingleton(schema);
+ s.AddOcelot()
+ .AddDelegatingHandler();
+ })
+ .ConfigureLogging((hostingContext, logging) =>
+ {
+ logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
+ logging.AddConsole();
+ })
+ .UseIISIntegration()
+ .Configure(app =>
+ {
+ app.UseOcelot().Wait();
+ })
+ .Build()
+ .Run();
+ }
+ }
+}
diff --git a/samples/OcelotGraphQL/Properties/launchSettings.json b/samples/GraphQL/Properties/launchSettings.json
similarity index 100%
rename from samples/OcelotGraphQL/Properties/launchSettings.json
rename to samples/GraphQL/Properties/launchSettings.json
diff --git a/samples/OcelotGraphQL/README.md b/samples/GraphQL/README.md
similarity index 96%
rename from samples/OcelotGraphQL/README.md
rename to samples/GraphQL/README.md
index 7f16ed985..1a470c344 100644
--- a/samples/OcelotGraphQL/README.md
+++ b/samples/GraphQL/README.md
@@ -1,71 +1,71 @@
-# Ocelot using GraphQL example
-
-Loads of people keep asking me if Ocelot will every support GraphQL, in my mind Ocelot and GraphQL are two different things that can work together.
-I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorization / authentication or I would
-bring in the awesome [graphql-dotnet](https://github.com/graphql-dotnet/graphql-dotnet) library and use it in a [DelegatingHandler](http://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html). This way you could have Ocelot and GraphQL without the extra hop to GraphQL. This same is an example of how to do that.
-
-## Example
-
-If you run this project with
-
-$ dotnet run
-
-Use postman or something to make the following requests and you can see Ocelot and GraphQL in action together...
-
-GET http://localhost:5000/graphql?query={ hero(id: 4) { id name } }
-
-RESPONSE
-```json
- {
- "data": {
- "hero": {
- "id": 4,
- "name": "Tom Pallister"
- }
- }
- }
-```
-
-POST http://localhost:5000/graphql
-
-BODY
-```json
- { hero(id: 4) { id name } }
-```
-
-RESPONSE
-```json
- {
- "data": {
- "hero": {
- "id": 4,
- "name": "Tom Pallister"
- }
- }
- }
-```
-
-## Notes
-
-Please note this project never goes out to another service, it just gets the data for GraphQL in memory. You would need to add the details of your GraphQL server in ocelot.json e.g.
-
-```json
-{
- "Routes": [
- {
- "DownstreamPathTemplate": "/graphql",
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "yourgraphqlhost.com",
- "Port": 80
- }
- ],
- "UpstreamPathTemplate": "/graphql",
- "DelegatingHandlers": [
- "GraphQlDelegatingHandler"
- ]
- }
- ]
- }
+# Ocelot using GraphQL example
+
+Loads of people keep asking me if Ocelot will every support GraphQL, in my mind Ocelot and GraphQL are two different things that can work together.
+I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorization / authentication or I would
+bring in the awesome [graphql-dotnet](https://github.com/graphql-dotnet/graphql-dotnet) library and use it in a [DelegatingHandler](http://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html). This way you could have Ocelot and GraphQL without the extra hop to GraphQL. This same is an example of how to do that.
+
+## Example
+
+If you run this project with
+
+$ dotnet run
+
+Use postman or something to make the following requests and you can see Ocelot and GraphQL in action together...
+
+GET http://localhost:5000/graphql?query={ hero(id: 4) { id name } }
+
+RESPONSE
+```json
+ {
+ "data": {
+ "hero": {
+ "id": 4,
+ "name": "Tom Pallister"
+ }
+ }
+ }
+```
+
+POST http://localhost:5000/graphql
+
+BODY
+```json
+ { hero(id: 4) { id name } }
+```
+
+RESPONSE
+```json
+ {
+ "data": {
+ "hero": {
+ "id": 4,
+ "name": "Tom Pallister"
+ }
+ }
+ }
+```
+
+## Notes
+
+Please note this project never goes out to another service, it just gets the data for GraphQL in memory. You would need to add the details of your GraphQL server in ocelot.json e.g.
+
+```json
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/graphql",
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "yourgraphqlhost.com",
+ "Port": 80
+ }
+ ],
+ "UpstreamPathTemplate": "/graphql",
+ "DelegatingHandlers": [
+ "GraphQlDelegatingHandler"
+ ]
+ }
+ ]
+ }
```
\ No newline at end of file
diff --git a/samples/OcelotGraphQL/ocelot.json b/samples/GraphQL/ocelot.json
similarity index 96%
rename from samples/OcelotGraphQL/ocelot.json
rename to samples/GraphQL/ocelot.json
index c716bf258..3529e0ba8 100644
--- a/samples/OcelotGraphQL/ocelot.json
+++ b/samples/GraphQL/ocelot.json
@@ -1,19 +1,19 @@
-{
- "Routes": [
- {
- "DownstreamPathTemplate": "/",
- "DownstreamScheme": "http",
- "DownstreamHostAndPorts": [
- {
- "Host": "jsonplaceholder.typicode.com",
- "Port": 80
- }
- ],
- "UpstreamPathTemplate": "/graphql",
- "DelegatingHandlers": [
- "GraphQlDelegatingHandler"
- ]
- }
- ]
- }
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/",
+ "DownstreamScheme": "http",
+ "DownstreamHostAndPorts": [
+ {
+ "Host": "jsonplaceholder.typicode.com",
+ "Port": 80
+ }
+ ],
+ "UpstreamPathTemplate": "/graphql",
+ "DelegatingHandlers": [
+ "GraphQlDelegatingHandler"
+ ]
+ }
+ ]
+ }
\ No newline at end of file
diff --git a/samples/OcelotKube/.dockerignore b/samples/Kubernetes/.dockerignore
similarity index 100%
rename from samples/OcelotKube/.dockerignore
rename to samples/Kubernetes/.dockerignore
diff --git a/samples/OcelotKube/ApiGateway/Dockerfile b/samples/Kubernetes/ApiGateway/Dockerfile
similarity index 100%
rename from samples/OcelotKube/ApiGateway/Dockerfile
rename to samples/Kubernetes/ApiGateway/Dockerfile
diff --git a/samples/OcelotKube/ApiGateway/Ocelot.Samples.OcelotKube.ApiGateway.csproj b/samples/Kubernetes/ApiGateway/Ocelot.Samples.Kubernetes.ApiGateway.csproj
similarity index 100%
rename from samples/OcelotKube/ApiGateway/Ocelot.Samples.OcelotKube.ApiGateway.csproj
rename to samples/Kubernetes/ApiGateway/Ocelot.Samples.Kubernetes.ApiGateway.csproj
diff --git a/samples/OcelotKube/ApiGateway/Program.cs b/samples/Kubernetes/ApiGateway/Program.cs
similarity index 100%
rename from samples/OcelotKube/ApiGateway/Program.cs
rename to samples/Kubernetes/ApiGateway/Program.cs
diff --git a/samples/OcelotKube/ApiGateway/Properties/launchSettings.json b/samples/Kubernetes/ApiGateway/Properties/launchSettings.json
similarity index 100%
rename from samples/OcelotKube/ApiGateway/Properties/launchSettings.json
rename to samples/Kubernetes/ApiGateway/Properties/launchSettings.json
diff --git a/samples/OcelotKube/ApiGateway/Startup.cs b/samples/Kubernetes/ApiGateway/Startup.cs
similarity index 100%
rename from samples/OcelotKube/ApiGateway/Startup.cs
rename to samples/Kubernetes/ApiGateway/Startup.cs
diff --git a/samples/OcelotKube/ApiGateway/appsettings.Development.json b/samples/Kubernetes/ApiGateway/appsettings.Development.json
similarity index 100%
rename from samples/OcelotKube/ApiGateway/appsettings.Development.json
rename to samples/Kubernetes/ApiGateway/appsettings.Development.json
diff --git a/samples/OcelotKube/DownstreamService/appsettings.json b/samples/Kubernetes/ApiGateway/appsettings.json
similarity index 100%
rename from samples/OcelotKube/DownstreamService/appsettings.json
rename to samples/Kubernetes/ApiGateway/appsettings.json
diff --git a/samples/OcelotKube/ApiGateway/ocelot.json b/samples/Kubernetes/ApiGateway/ocelot.json
similarity index 95%
rename from samples/OcelotKube/ApiGateway/ocelot.json
rename to samples/Kubernetes/ApiGateway/ocelot.json
index 6a28b9eec..f4a5af0b8 100644
--- a/samples/OcelotKube/ApiGateway/ocelot.json
+++ b/samples/Kubernetes/ApiGateway/ocelot.json
@@ -1,20 +1,20 @@
-{
- "Routes": [
- {
- "DownstreamPathTemplate": "/api/values",
- "DownstreamScheme": "http",
- "UpstreamPathTemplate": "/values",
- "ServiceName": "downstreamservice",
- "UpstreamHttpMethod": [ "Get" ]
- }
- ],
- "GlobalConfiguration": {
- "ServiceDiscoveryProvider": {
- "Host": "192.168.0.13",
- "Port": 443,
- "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
- "Namespace": "dev",
- "Type": "kube"
- }
- }
-}
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/api/values",
+ "DownstreamScheme": "http",
+ "UpstreamPathTemplate": "/values",
+ "ServiceName": "downstreamservice",
+ "UpstreamHttpMethod": [ "Get" ]
+ }
+ ],
+ "GlobalConfiguration": {
+ "ServiceDiscoveryProvider": {
+ "Host": "192.168.0.13",
+ "Port": 443,
+ "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
+ "Namespace": "dev",
+ "Type": "kube"
+ }
+ }
+}
diff --git a/samples/OcelotKube/Dockerfile b/samples/Kubernetes/Dockerfile
similarity index 100%
rename from samples/OcelotKube/Dockerfile
rename to samples/Kubernetes/Dockerfile
diff --git a/samples/OcelotKube/DownstreamService/Controllers/ValuesController.cs b/samples/Kubernetes/DownstreamService/Controllers/ValuesController.cs
similarity index 100%
rename from samples/OcelotKube/DownstreamService/Controllers/ValuesController.cs
rename to samples/Kubernetes/DownstreamService/Controllers/ValuesController.cs
diff --git a/samples/OcelotKube/DownstreamService/Controllers/WeatherForecastController.cs b/samples/Kubernetes/DownstreamService/Controllers/WeatherForecastController.cs
similarity index 100%
rename from samples/OcelotKube/DownstreamService/Controllers/WeatherForecastController.cs
rename to samples/Kubernetes/DownstreamService/Controllers/WeatherForecastController.cs
diff --git a/samples/OcelotKube/DownstreamService/Dockerfile b/samples/Kubernetes/DownstreamService/Dockerfile
similarity index 100%
rename from samples/OcelotKube/DownstreamService/Dockerfile
rename to samples/Kubernetes/DownstreamService/Dockerfile
diff --git a/samples/OcelotKube/DownstreamService/Models/WeatherForecast.cs b/samples/Kubernetes/DownstreamService/Models/WeatherForecast.cs
similarity index 100%
rename from samples/OcelotKube/DownstreamService/Models/WeatherForecast.cs
rename to samples/Kubernetes/DownstreamService/Models/WeatherForecast.cs
diff --git a/samples/OcelotKube/DownstreamService/Ocelot.Samples.OcelotKube.DownstreamService.csproj b/samples/Kubernetes/DownstreamService/Ocelot.Samples.Kubernetes.DownstreamService.csproj
similarity index 86%
rename from samples/OcelotKube/DownstreamService/Ocelot.Samples.OcelotKube.DownstreamService.csproj
rename to samples/Kubernetes/DownstreamService/Ocelot.Samples.Kubernetes.DownstreamService.csproj
index 3c1cfefb2..72b36b2fc 100644
--- a/samples/OcelotKube/DownstreamService/Ocelot.Samples.OcelotKube.DownstreamService.csproj
+++ b/samples/Kubernetes/DownstreamService/Ocelot.Samples.Kubernetes.DownstreamService.csproj
@@ -1,6 +1,6 @@
- net7.0
+ net6.0;net7.0;net8.0
disable
disable
InProcess
diff --git a/samples/OcelotKube/DownstreamService/Program.cs b/samples/Kubernetes/DownstreamService/Program.cs
similarity index 100%
rename from samples/OcelotKube/DownstreamService/Program.cs
rename to samples/Kubernetes/DownstreamService/Program.cs
diff --git a/samples/OcelotKube/DownstreamService/Properties/launchSettings.json b/samples/Kubernetes/DownstreamService/Properties/launchSettings.json
similarity index 100%
rename from samples/OcelotKube/DownstreamService/Properties/launchSettings.json
rename to samples/Kubernetes/DownstreamService/Properties/launchSettings.json
diff --git a/samples/OcelotKube/DownstreamService/appsettings.Development.json b/samples/Kubernetes/DownstreamService/appsettings.Development.json
similarity index 100%
rename from samples/OcelotKube/DownstreamService/appsettings.Development.json
rename to samples/Kubernetes/DownstreamService/appsettings.Development.json
diff --git a/samples/OcelotBasic/appsettings.json b/samples/Kubernetes/DownstreamService/appsettings.json
similarity index 92%
rename from samples/OcelotBasic/appsettings.json
rename to samples/Kubernetes/DownstreamService/appsettings.json
index 7376aada1..def9159a7 100644
--- a/samples/OcelotBasic/appsettings.json
+++ b/samples/Kubernetes/DownstreamService/appsettings.json
@@ -1,8 +1,8 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Warning"
- }
- },
- "AllowedHosts": "*"
-}
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/samples/OcelotKube/OcelotKube.sln b/samples/Kubernetes/OcelotKube.sln
similarity index 100%
rename from samples/OcelotKube/OcelotKube.sln
rename to samples/Kubernetes/OcelotKube.sln
diff --git a/samples/Ocelot.Samples.sln b/samples/Ocelot.Samples.sln
new file mode 100644
index 000000000..ca208f4a6
--- /dev/null
+++ b/samples/Ocelot.Samples.sln
@@ -0,0 +1,91 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34728.123
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.AdministrationApi", "Administration\Ocelot.Samples.AdministrationApi.csproj", "{238467FE-19EE-4102-9AF7-51EB2C6F0354}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.Basic.ApiGateway", "Basic\Ocelot.Samples.Basic.ApiGateway.csproj", "{A7D2C43A-E35C-4A89-AEE5-5C87052ECD89}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.Eureka.ApiGateway", "Eureka\ApiGateway\Ocelot.Samples.Eureka.ApiGateway.csproj", "{EA0E146F-2C2B-4176-B6EC-F62A587F5077}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.Eureka.DownstreamService", "Eureka\DownstreamService\Ocelot.Samples.Eureka.DownstreamService.csproj", "{B7317B64-2208-472D-90AC-F42B61956B79}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.GraphQL", "GraphQL\Ocelot.Samples.GraphQL.csproj", "{6CCA3677-420A-4294-8D41-67CF3D818575}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.Kubernetes.ApiGateway", "Kubernetes\ApiGateway\Ocelot.Samples.Kubernetes.ApiGateway.csproj", "{721C1737-70CB-4B11-A19B-C7AAC6856CC7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.Kubernetes.DownstreamService", "Kubernetes\DownstreamService\Ocelot.Samples.Kubernetes.DownstreamService.csproj", "{CE949A5D-9D25-46E3-B59A-DA63F7ED9A59}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.OpenTracing", "OpenTracing\Ocelot.Samples.OpenTracing.csproj", "{707BD584-3CC0-4087-820C-049C3D68F6A3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceDiscovery.ApiGateway", "ServiceDiscovery\ApiGateway\Ocelot.Samples.ServiceDiscovery.ApiGateway.csproj", "{96B9F16E-C95D-425A-A419-40CB3C90CB77}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceDiscovery.DownstreamService", "ServiceDiscovery\DownstreamService\Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj", "{60E14B1A-C295-453B-910E-58E09F5A28AA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceFabric.ApiGateway", "ServiceFabric\ApiGateway\Ocelot.Samples.ServiceFabric.ApiGateway.csproj", "{115F7934-3326-492A-B131-64F0EAEBAD71}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Samples.ServiceFabric.DownstreamService", "ServiceFabric\DownstreamService\Ocelot.Samples.ServiceFabric.DownstreamService.csproj", "{6C777A20-F557-45CF-B87B-11E3C6B29A36}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {238467FE-19EE-4102-9AF7-51EB2C6F0354}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {238467FE-19EE-4102-9AF7-51EB2C6F0354}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {238467FE-19EE-4102-9AF7-51EB2C6F0354}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {238467FE-19EE-4102-9AF7-51EB2C6F0354}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A7D2C43A-E35C-4A89-AEE5-5C87052ECD89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7D2C43A-E35C-4A89-AEE5-5C87052ECD89}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A7D2C43A-E35C-4A89-AEE5-5C87052ECD89}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A7D2C43A-E35C-4A89-AEE5-5C87052ECD89}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EA0E146F-2C2B-4176-B6EC-F62A587F5077}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EA0E146F-2C2B-4176-B6EC-F62A587F5077}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EA0E146F-2C2B-4176-B6EC-F62A587F5077}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EA0E146F-2C2B-4176-B6EC-F62A587F5077}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B7317B64-2208-472D-90AC-F42B61956B79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B7317B64-2208-472D-90AC-F42B61956B79}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B7317B64-2208-472D-90AC-F42B61956B79}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B7317B64-2208-472D-90AC-F42B61956B79}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6CCA3677-420A-4294-8D41-67CF3D818575}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6CCA3677-420A-4294-8D41-67CF3D818575}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6CCA3677-420A-4294-8D41-67CF3D818575}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6CCA3677-420A-4294-8D41-67CF3D818575}.Release|Any CPU.Build.0 = Release|Any CPU
+ {721C1737-70CB-4B11-A19B-C7AAC6856CC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {721C1737-70CB-4B11-A19B-C7AAC6856CC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {721C1737-70CB-4B11-A19B-C7AAC6856CC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {721C1737-70CB-4B11-A19B-C7AAC6856CC7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CE949A5D-9D25-46E3-B59A-DA63F7ED9A59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CE949A5D-9D25-46E3-B59A-DA63F7ED9A59}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CE949A5D-9D25-46E3-B59A-DA63F7ED9A59}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CE949A5D-9D25-46E3-B59A-DA63F7ED9A59}.Release|Any CPU.Build.0 = Release|Any CPU
+ {707BD584-3CC0-4087-820C-049C3D68F6A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {707BD584-3CC0-4087-820C-049C3D68F6A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {707BD584-3CC0-4087-820C-049C3D68F6A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {707BD584-3CC0-4087-820C-049C3D68F6A3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {96B9F16E-C95D-425A-A419-40CB3C90CB77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {96B9F16E-C95D-425A-A419-40CB3C90CB77}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {96B9F16E-C95D-425A-A419-40CB3C90CB77}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {96B9F16E-C95D-425A-A419-40CB3C90CB77}.Release|Any CPU.Build.0 = Release|Any CPU
+ {60E14B1A-C295-453B-910E-58E09F5A28AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {60E14B1A-C295-453B-910E-58E09F5A28AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {60E14B1A-C295-453B-910E-58E09F5A28AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {60E14B1A-C295-453B-910E-58E09F5A28AA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {115F7934-3326-492A-B131-64F0EAEBAD71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {115F7934-3326-492A-B131-64F0EAEBAD71}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {115F7934-3326-492A-B131-64F0EAEBAD71}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {115F7934-3326-492A-B131-64F0EAEBAD71}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6C777A20-F557-45CF-B87B-11E3C6B29A36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6C777A20-F557-45CF-B87B-11E3C6B29A36}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6C777A20-F557-45CF-B87B-11E3C6B29A36}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6C777A20-F557-45CF-B87B-11E3C6B29A36}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {2C1620D4-EB38-4C3E-9FC5-029FB6B2F426}
+ EndGlobalSection
+EndGlobal
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/.dockerignore b/samples/OcelotServiceDiscovery/DownstreamService/.dockerignore
deleted file mode 100644
index e7b690f11..000000000
--- a/samples/OcelotServiceDiscovery/DownstreamService/.dockerignore
+++ /dev/null
@@ -1,25 +0,0 @@
-**/.classpath
-**/.dockerignore
-**/.env
-**/.git
-**/.gitignore
-**/.project
-**/.settings
-**/.toolstarget
-**/.vs
-**/.vscode
-**/*.*proj.user
-**/*.dbmdl
-**/*.jfm
-**/azds.yaml
-**/bin
-**/charts
-**/docker-compose*
-**/Dockerfile*
-**/node_modules
-**/npm-debug.log
-**/obj
-**/secrets.dev.yaml
-**/values.dev.yaml
-LICENSE
-README.md
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Dockerfile b/samples/OcelotServiceDiscovery/DownstreamService/Dockerfile
deleted file mode 100644
index b7535cfcd..000000000
--- a/samples/OcelotServiceDiscovery/DownstreamService/Dockerfile
+++ /dev/null
@@ -1,22 +0,0 @@
-#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
-
-FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
-WORKDIR /app
-EXPOSE 80
-EXPOSE 443
-
-FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
-WORKDIR /src
-COPY ["Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj", "."]
-RUN dotnet restore "./Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj"
-COPY . .
-WORKDIR "/src/."
-RUN dotnet build "Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj" -c Release -o /app/build
-
-FROM build AS publish
-RUN dotnet publish "Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj" -c Release -o /app/publish /p:UseAppHost=false
-
-FROM base AS final
-WORKDIR /app
-COPY --from=publish /app/publish .
-ENTRYPOINT ["dotnet", "Ocelot.Samples.ServiceDiscovery.DownstreamService.dll"]
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj b/samples/OcelotServiceDiscovery/DownstreamService/Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj
deleted file mode 100644
index c0669cb7d..000000000
--- a/samples/OcelotServiceDiscovery/DownstreamService/Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- net7.0
- disable
- enable
- Linux
- .
- d5492aa8-b50c-41ae-a044-9954846db9ac
-
-
-
-
-
-
-
-
diff --git a/samples/OcelotOpenTracing/OcelotOpenTracing.csproj b/samples/OpenTracing/Ocelot.Samples.OpenTracing.csproj
similarity index 100%
rename from samples/OcelotOpenTracing/OcelotOpenTracing.csproj
rename to samples/OpenTracing/Ocelot.Samples.OpenTracing.csproj
diff --git a/samples/OcelotOpenTracing/Program.cs b/samples/OpenTracing/Program.cs
similarity index 100%
rename from samples/OcelotOpenTracing/Program.cs
rename to samples/OpenTracing/Program.cs
diff --git a/samples/OcelotOpenTracing/appsettings.Development.json b/samples/OpenTracing/appsettings.Development.json
similarity index 100%
rename from samples/OcelotOpenTracing/appsettings.Development.json
rename to samples/OpenTracing/appsettings.Development.json
diff --git a/samples/OcelotOpenTracing/appsettings.json b/samples/OpenTracing/appsettings.json
similarity index 100%
rename from samples/OcelotOpenTracing/appsettings.json
rename to samples/OpenTracing/appsettings.json
diff --git a/samples/OcelotOpenTracing/ocelot.json b/samples/OpenTracing/ocelot.json
similarity index 100%
rename from samples/OcelotOpenTracing/ocelot.json
rename to samples/OpenTracing/ocelot.json
diff --git a/samples/OcelotServiceDiscovery/.dockerignore b/samples/ServiceDiscovery/.dockerignore
similarity index 100%
rename from samples/OcelotServiceDiscovery/.dockerignore
rename to samples/ServiceDiscovery/.dockerignore
diff --git a/samples/OcelotServiceDiscovery/ApiGateway/Ocelot.Samples.ServiceDiscovery.ApiGateway.csproj b/samples/ServiceDiscovery/ApiGateway/Ocelot.Samples.ServiceDiscovery.ApiGateway.csproj
similarity index 59%
rename from samples/OcelotServiceDiscovery/ApiGateway/Ocelot.Samples.ServiceDiscovery.ApiGateway.csproj
rename to samples/ServiceDiscovery/ApiGateway/Ocelot.Samples.ServiceDiscovery.ApiGateway.csproj
index aac1dd20a..815c793e9 100644
--- a/samples/OcelotServiceDiscovery/ApiGateway/Ocelot.Samples.ServiceDiscovery.ApiGateway.csproj
+++ b/samples/ServiceDiscovery/ApiGateway/Ocelot.Samples.ServiceDiscovery.ApiGateway.csproj
@@ -1,6 +1,8 @@
- net6.0;net7.0;net8.0
+ net8.0
+ disable
+ disable
diff --git a/samples/OcelotServiceDiscovery/ApiGateway/Program.cs b/samples/ServiceDiscovery/ApiGateway/Program.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/ApiGateway/Program.cs
rename to samples/ServiceDiscovery/ApiGateway/Program.cs
diff --git a/samples/OcelotServiceDiscovery/ApiGateway/Properties/launchSettings.json b/samples/ServiceDiscovery/ApiGateway/Properties/launchSettings.json
similarity index 100%
rename from samples/OcelotServiceDiscovery/ApiGateway/Properties/launchSettings.json
rename to samples/ServiceDiscovery/ApiGateway/Properties/launchSettings.json
diff --git a/samples/OcelotServiceDiscovery/ApiGateway/ServiceDiscovery/MyServiceDiscoveryProvider.cs b/samples/ServiceDiscovery/ApiGateway/ServiceDiscovery/MyServiceDiscoveryProvider.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/ApiGateway/ServiceDiscovery/MyServiceDiscoveryProvider.cs
rename to samples/ServiceDiscovery/ApiGateway/ServiceDiscovery/MyServiceDiscoveryProvider.cs
diff --git a/samples/OcelotServiceDiscovery/ApiGateway/ServiceDiscovery/MyServiceDiscoveryProviderFactory.cs b/samples/ServiceDiscovery/ApiGateway/ServiceDiscovery/MyServiceDiscoveryProviderFactory.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/ApiGateway/ServiceDiscovery/MyServiceDiscoveryProviderFactory.cs
rename to samples/ServiceDiscovery/ApiGateway/ServiceDiscovery/MyServiceDiscoveryProviderFactory.cs
diff --git a/samples/OcelotServiceDiscovery/ApiGateway/appsettings.json b/samples/ServiceDiscovery/ApiGateway/appsettings.json
similarity index 100%
rename from samples/OcelotServiceDiscovery/ApiGateway/appsettings.json
rename to samples/ServiceDiscovery/ApiGateway/appsettings.json
diff --git a/samples/OcelotServiceDiscovery/ApiGateway/ocelot.json b/samples/ServiceDiscovery/ApiGateway/ocelot.json
similarity index 100%
rename from samples/OcelotServiceDiscovery/ApiGateway/ocelot.json
rename to samples/ServiceDiscovery/ApiGateway/ocelot.json
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Controllers/CategoriesController.cs b/samples/ServiceDiscovery/DownstreamService/Controllers/CategoriesController.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/Controllers/CategoriesController.cs
rename to samples/ServiceDiscovery/DownstreamService/Controllers/CategoriesController.cs
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Controllers/HealthController.cs b/samples/ServiceDiscovery/DownstreamService/Controllers/HealthController.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/Controllers/HealthController.cs
rename to samples/ServiceDiscovery/DownstreamService/Controllers/HealthController.cs
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Controllers/WeatherForecastController.cs b/samples/ServiceDiscovery/DownstreamService/Controllers/WeatherForecastController.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/Controllers/WeatherForecastController.cs
rename to samples/ServiceDiscovery/DownstreamService/Controllers/WeatherForecastController.cs
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Models/HealthResult.cs b/samples/ServiceDiscovery/DownstreamService/Models/HealthResult.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/Models/HealthResult.cs
rename to samples/ServiceDiscovery/DownstreamService/Models/HealthResult.cs
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Models/MicroserviceResult.cs b/samples/ServiceDiscovery/DownstreamService/Models/MicroserviceResult.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/Models/MicroserviceResult.cs
rename to samples/ServiceDiscovery/DownstreamService/Models/MicroserviceResult.cs
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Models/ReadyResult.cs b/samples/ServiceDiscovery/DownstreamService/Models/ReadyResult.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/Models/ReadyResult.cs
rename to samples/ServiceDiscovery/DownstreamService/Models/ReadyResult.cs
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Models/WeatherForecast.cs b/samples/ServiceDiscovery/DownstreamService/Models/WeatherForecast.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/Models/WeatherForecast.cs
rename to samples/ServiceDiscovery/DownstreamService/Models/WeatherForecast.cs
diff --git a/samples/ServiceDiscovery/DownstreamService/Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj b/samples/ServiceDiscovery/DownstreamService/Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj
new file mode 100644
index 000000000..163a956bb
--- /dev/null
+++ b/samples/ServiceDiscovery/DownstreamService/Ocelot.Samples.ServiceDiscovery.DownstreamService.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net8.0
+ disable
+ enable
+
+
+
+
+
+
+
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Program.cs b/samples/ServiceDiscovery/DownstreamService/Program.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/Program.cs
rename to samples/ServiceDiscovery/DownstreamService/Program.cs
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Properties/launchSettings.json b/samples/ServiceDiscovery/DownstreamService/Properties/launchSettings.json
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/Properties/launchSettings.json
rename to samples/ServiceDiscovery/DownstreamService/Properties/launchSettings.json
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/Startup.cs b/samples/ServiceDiscovery/DownstreamService/Startup.cs
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/Startup.cs
rename to samples/ServiceDiscovery/DownstreamService/Startup.cs
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/appsettings.Development.json b/samples/ServiceDiscovery/DownstreamService/appsettings.Development.json
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/appsettings.Development.json
rename to samples/ServiceDiscovery/DownstreamService/appsettings.Development.json
diff --git a/samples/OcelotServiceDiscovery/DownstreamService/appsettings.json b/samples/ServiceDiscovery/DownstreamService/appsettings.json
similarity index 100%
rename from samples/OcelotServiceDiscovery/DownstreamService/appsettings.json
rename to samples/ServiceDiscovery/DownstreamService/appsettings.json
diff --git a/samples/OcelotServiceDiscovery/Ocelot.Samples.ServiceDiscovery.sln b/samples/ServiceDiscovery/Ocelot.Samples.ServiceDiscovery.sln
similarity index 100%
rename from samples/OcelotServiceDiscovery/Ocelot.Samples.ServiceDiscovery.sln
rename to samples/ServiceDiscovery/Ocelot.Samples.ServiceDiscovery.sln
diff --git a/samples/OcelotServiceDiscovery/README.md b/samples/ServiceDiscovery/README.md
similarity index 100%
rename from samples/OcelotServiceDiscovery/README.md
rename to samples/ServiceDiscovery/README.md
diff --git a/samples/OcelotServiceFabric/.gitignore b/samples/ServiceFabric/.gitignore
similarity index 100%
rename from samples/OcelotServiceFabric/.gitignore
rename to samples/ServiceFabric/.gitignore
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj b/samples/ServiceFabric/ApiGateway/Ocelot.Samples.ServiceFabric.ApiGateway.csproj
similarity index 92%
rename from samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj
rename to samples/ServiceFabric/ApiGateway/Ocelot.Samples.ServiceFabric.ApiGateway.csproj
index c97238c53..c9f886f75 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj
+++ b/samples/ServiceFabric/ApiGateway/Ocelot.Samples.ServiceFabric.ApiGateway.csproj
@@ -18,6 +18,6 @@
-
+
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.cs b/samples/ServiceFabric/ApiGateway/OcelotApplicationApiGateway.cs
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.cs
rename to samples/ServiceFabric/ApiGateway/OcelotApplicationApiGateway.cs
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/Program.cs b/samples/ServiceFabric/ApiGateway/Program.cs
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/Program.cs
rename to samples/ServiceFabric/ApiGateway/Program.cs
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/Properties/launchSettings.json b/samples/ServiceFabric/ApiGateway/Properties/launchSettings.json
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/Properties/launchSettings.json
rename to samples/ServiceFabric/ApiGateway/Properties/launchSettings.json
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventListener.cs b/samples/ServiceFabric/ApiGateway/ServiceEventListener.cs
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventListener.cs
rename to samples/ServiceFabric/ApiGateway/ServiceEventListener.cs
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventSource.cs b/samples/ServiceFabric/ApiGateway/ServiceEventSource.cs
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ServiceEventSource.cs
rename to samples/ServiceFabric/ApiGateway/ServiceEventSource.cs
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/WebCommunicationListener.cs b/samples/ServiceFabric/ApiGateway/WebCommunicationListener.cs
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/WebCommunicationListener.cs
rename to samples/ServiceFabric/ApiGateway/WebCommunicationListener.cs
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/appsettings.json b/samples/ServiceFabric/ApiGateway/appsettings.json
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/appsettings.json
rename to samples/ServiceFabric/ApiGateway/appsettings.json
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json b/samples/ServiceFabric/ApiGateway/ocelot.json
similarity index 95%
rename from samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json
rename to samples/ServiceFabric/ApiGateway/ocelot.json
index b541e95c4..1b174cd62 100644
--- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json
+++ b/samples/ServiceFabric/ApiGateway/ocelot.json
@@ -1,21 +1,21 @@
-{
- "Routes": [
- {
- "DownstreamPathTemplate": "/api/values",
- "UpstreamPathTemplate": "/EquipmentInterfaces",
- "UpstreamHttpMethod": [
- "Get"
- ],
- "DownstreamScheme": "http",
- "ServiceName": "OcelotServiceApplication/OcelotApplicationService"
- }
- ],
- "GlobalConfiguration": {
- "RequestIdKey": "OcRequestId",
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 19081,
- "Type": "ServiceFabric"
- }
- }
-}
+{
+ "Routes": [
+ {
+ "DownstreamPathTemplate": "/api/values",
+ "UpstreamPathTemplate": "/EquipmentInterfaces",
+ "UpstreamHttpMethod": [
+ "Get"
+ ],
+ "DownstreamScheme": "http",
+ "ServiceName": "OcelotServiceApplication/OcelotApplicationService"
+ }
+ ],
+ "GlobalConfiguration": {
+ "RequestIdKey": "OcRequestId",
+ "ServiceDiscoveryProvider": {
+ "Host": "localhost",
+ "Port": 19081,
+ "Type": "ServiceFabric"
+ }
+ }
+}
diff --git a/samples/OcelotServiceFabric/CONTRIBUTING.md b/samples/ServiceFabric/CONTRIBUTING.md
similarity index 100%
rename from samples/OcelotServiceFabric/CONTRIBUTING.md
rename to samples/ServiceFabric/CONTRIBUTING.md
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/ApiGateway.cs b/samples/ServiceFabric/DownstreamService/ApiGateway.cs
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationService/ApiGateway.cs
rename to samples/ServiceFabric/DownstreamService/ApiGateway.cs
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/Controllers/ValuesController.cs b/samples/ServiceFabric/DownstreamService/Controllers/ValuesController.cs
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationService/Controllers/ValuesController.cs
rename to samples/ServiceFabric/DownstreamService/Controllers/ValuesController.cs
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj b/samples/ServiceFabric/DownstreamService/Ocelot.Samples.ServiceFabric.DownstreamService.csproj
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj
rename to samples/ServiceFabric/DownstreamService/Ocelot.Samples.ServiceFabric.DownstreamService.csproj
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/Program.cs b/samples/ServiceFabric/DownstreamService/Program.cs
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationService/Program.cs
rename to samples/ServiceFabric/DownstreamService/Program.cs
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/Properties/launchSettings.json b/samples/ServiceFabric/DownstreamService/Properties/launchSettings.json
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationService/Properties/launchSettings.json
rename to samples/ServiceFabric/DownstreamService/Properties/launchSettings.json
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/ServiceEventSource.cs b/samples/ServiceFabric/DownstreamService/ServiceEventSource.cs
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationService/ServiceEventSource.cs
rename to samples/ServiceFabric/DownstreamService/ServiceEventSource.cs
diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/Startup.cs b/samples/ServiceFabric/DownstreamService/Startup.cs
similarity index 100%
rename from samples/OcelotServiceFabric/src/OcelotApplicationService/Startup.cs
rename to samples/ServiceFabric/DownstreamService/Startup.cs
diff --git a/samples/OcelotServiceFabric/LICENSE.md b/samples/ServiceFabric/LICENSE.md
similarity index 100%
rename from samples/OcelotServiceFabric/LICENSE.md
rename to samples/ServiceFabric/LICENSE.md
diff --git a/samples/OcelotServiceFabric/OcelotApplication/ApplicationManifest.xml b/samples/ServiceFabric/OcelotApplication/ApplicationManifest.xml
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/ApplicationManifest.xml
rename to samples/ServiceFabric/OcelotApplication/ApplicationManifest.xml
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Code/entryPoint.cmd b/samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Code/entryPoint.cmd
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Code/entryPoint.cmd
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Code/entryPoint.cmd
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Code/entryPoint.sh b/samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Code/entryPoint.sh
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Code/entryPoint.sh
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Code/entryPoint.sh
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Config/Settings.xml b/samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Config/Settings.xml
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Config/Settings.xml
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Config/Settings.xml
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Config/_readme.txt b/samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Config/_readme.txt
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Config/_readme.txt
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Config/_readme.txt
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Data/_readme.txt b/samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Data/_readme.txt
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Data/_readme.txt
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/Data/_readme.txt
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest-Linux.xml b/samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest-Linux.xml
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest-Linux.xml
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest-Linux.xml
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest-Windows.xml b/samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest-Windows.xml
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest-Windows.xml
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest-Windows.xml
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest.xml b/samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest.xml
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest.xml
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationApiGatewayPkg/ServiceManifest.xml
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Code/entryPoint.cmd b/samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Code/entryPoint.cmd
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Code/entryPoint.cmd
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Code/entryPoint.cmd
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Code/entryPoint.sh b/samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Code/entryPoint.sh
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Code/entryPoint.sh
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Code/entryPoint.sh
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Config/Settings.xml b/samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Config/Settings.xml
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Config/Settings.xml
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Config/Settings.xml
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Config/_readme.txt b/samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Config/_readme.txt
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Config/_readme.txt
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Config/_readme.txt
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Data/_readme.txt b/samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Data/_readme.txt
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Data/_readme.txt
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/Data/_readme.txt
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest-Linux.xml b/samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest-Linux.xml
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest-Linux.xml
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest-Linux.xml
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest-Windows.xml b/samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest-Windows.xml
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest-Windows.xml
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest-Windows.xml
diff --git a/samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest.xml b/samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest.xml
similarity index 100%
rename from samples/OcelotServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest.xml
rename to samples/ServiceFabric/OcelotApplication/OcelotApplicationServicePkg/ServiceManifest.xml
diff --git a/samples/OcelotServiceFabric/README.md b/samples/ServiceFabric/README.md
similarity index 100%
rename from samples/OcelotServiceFabric/README.md
rename to samples/ServiceFabric/README.md
diff --git a/samples/OcelotServiceFabric/build.bat b/samples/ServiceFabric/build.bat
similarity index 100%
rename from samples/OcelotServiceFabric/build.bat
rename to samples/ServiceFabric/build.bat
diff --git a/samples/OcelotServiceFabric/build.sh b/samples/ServiceFabric/build.sh
similarity index 100%
rename from samples/OcelotServiceFabric/build.sh
rename to samples/ServiceFabric/build.sh
diff --git a/samples/OcelotServiceFabric/dotnet-include.sh b/samples/ServiceFabric/dotnet-include.sh
similarity index 100%
rename from samples/OcelotServiceFabric/dotnet-include.sh
rename to samples/ServiceFabric/dotnet-include.sh
diff --git a/samples/OcelotServiceFabric/install.ps1 b/samples/ServiceFabric/install.ps1
similarity index 100%
rename from samples/OcelotServiceFabric/install.ps1
rename to samples/ServiceFabric/install.ps1
diff --git a/samples/OcelotServiceFabric/install.sh b/samples/ServiceFabric/install.sh
similarity index 100%
rename from samples/OcelotServiceFabric/install.sh
rename to samples/ServiceFabric/install.sh
diff --git a/samples/OcelotServiceFabric/uninstall.ps1 b/samples/ServiceFabric/uninstall.ps1
similarity index 100%
rename from samples/OcelotServiceFabric/uninstall.ps1
rename to samples/ServiceFabric/uninstall.ps1
diff --git a/samples/OcelotServiceFabric/uninstall.sh b/samples/ServiceFabric/uninstall.sh
similarity index 100%
rename from samples/OcelotServiceFabric/uninstall.sh
rename to samples/ServiceFabric/uninstall.sh
diff --git a/src/Ocelot.Provider.Consul/Consul.cs b/src/Ocelot.Provider.Consul/Consul.cs
index 273fca2ab..27b5b4422 100644
--- a/src/Ocelot.Provider.Consul/Consul.cs
+++ b/src/Ocelot.Provider.Consul/Consul.cs
@@ -1,5 +1,5 @@
-using Ocelot.Infrastructure.Extensions;
-using Ocelot.Logging;
+using Ocelot.Logging;
+using Ocelot.Provider.Consul.Interfaces;
using Ocelot.ServiceDiscovery.Providers;
using Ocelot.Values;
@@ -7,70 +7,49 @@ namespace Ocelot.Provider.Consul;
public class Consul : IServiceDiscoveryProvider
{
- private const string VersionPrefix = "version-";
- private readonly ConsulRegistryConfiguration _config;
+ private readonly ConsulRegistryConfiguration _configuration;
private readonly IConsulClient _consul;
private readonly IOcelotLogger _logger;
+ private readonly IConsulServiceBuilder _serviceBuilder;
- public Consul(ConsulRegistryConfiguration config, IOcelotLoggerFactory factory, IConsulClientFactory clientFactory)
+ public Consul(
+ ConsulRegistryConfiguration config,
+ IOcelotLoggerFactory factory,
+ IConsulClientFactory clientFactory,
+ IConsulServiceBuilder serviceBuilder)
{
- _config = config;
- _consul = clientFactory.Get(_config);
+ _configuration = config;
+ _consul = clientFactory.Get(_configuration);
_logger = factory.CreateLogger();
+ _serviceBuilder = serviceBuilder;
}
- public async Task> GetAsync()
+ public virtual async Task> GetAsync()
{
- var queryResult = await _consul.Health.Service(_config.KeyOfServiceInConsul, string.Empty, true);
+ var entriesTask = _consul.Health.Service(_configuration.KeyOfServiceInConsul, string.Empty, true);
+ var nodesTask = _consul.Catalog.Nodes();
+ await Task.WhenAll(entriesTask, nodesTask);
+
+ var entries = entriesTask.Result.Response ?? Array.Empty();
+ var nodes = nodesTask.Result.Response ?? Array.Empty();
var services = new List();
- foreach (var serviceEntry in queryResult.Response)
+ if (entries.Length != 0)
{
- var service = serviceEntry.Service;
- if (IsValid(service))
- {
- var nodes = await _consul.Catalog.Nodes();
- if (nodes.Response == null)
- {
- services.Add(BuildService(serviceEntry, null));
- }
- else
- {
- var serviceNode = nodes.Response.FirstOrDefault(n => n.Address == service.Address);
- services.Add(BuildService(serviceEntry, serviceNode));
- }
- }
- else
- {
- _logger.LogWarning(
- () => $"Unable to use service address: '{service.Address}' and port: {service.Port} as it is invalid for the service: '{service.Service}'. Address must contain host only e.g. 'localhost', and port must be greater than 0.");
- }
+ _logger.LogDebug(() => $"{nameof(Consul)} Provider: Found total {entries.Length} service entries for '{_configuration.KeyOfServiceInConsul}' service.");
+ _logger.LogDebug(() => $"{nameof(Consul)} Provider: Found total {nodes.Length} catalog nodes.");
+ var collection = BuildServices(entries, nodes);
+ services.AddRange(collection);
+ }
+ else
+ {
+ _logger.LogWarning(() => $"{nameof(Consul)} Provider: No service entries found for '{_configuration.KeyOfServiceInConsul}' service!");
}
- return services.ToList();
- }
-
- private static Service BuildService(ServiceEntry serviceEntry, Node serviceNode)
- {
- var service = serviceEntry.Service;
- return new Service(
- service.Service,
- new ServiceHostAndPort(
- serviceNode == null ? service.Address : serviceNode.Name,
- service.Port),
- service.ID,
- GetVersionFromStrings(service.Tags),
- service.Tags ?? Enumerable.Empty());
+ return services;
}
- private static bool IsValid(AgentService service)
- => !string.IsNullOrEmpty(service.Address)
- && !service.Address.Contains($"{Uri.UriSchemeHttp}://")
- && !service.Address.Contains($"{Uri.UriSchemeHttps}://")
- && service.Port > 0;
-
- private static string GetVersionFromStrings(IEnumerable strings)
- => strings?.FirstOrDefault(x => x.StartsWith(VersionPrefix, StringComparison.Ordinal))
- .TrimStart(VersionPrefix);
+ protected virtual IEnumerable BuildServices(ServiceEntry[] entries, Node[] nodes)
+ => _serviceBuilder.BuildServices(entries, nodes);
}
diff --git a/src/Ocelot.Provider.Consul/ConsulClientFactory.cs b/src/Ocelot.Provider.Consul/ConsulClientFactory.cs
index 4a7478c59..f7c5c0c0c 100644
--- a/src/Ocelot.Provider.Consul/ConsulClientFactory.cs
+++ b/src/Ocelot.Provider.Consul/ConsulClientFactory.cs
@@ -1,4 +1,6 @@
-namespace Ocelot.Provider.Consul;
+using Ocelot.Provider.Consul.Interfaces;
+
+namespace Ocelot.Provider.Consul;
public class ConsulClientFactory : IConsulClientFactory
{
diff --git a/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs b/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs
index 2f9569362..c95146f46 100644
--- a/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs
+++ b/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs
@@ -5,6 +5,7 @@
using Ocelot.Configuration.File;
using Ocelot.Configuration.Repository;
using Ocelot.Logging;
+using Ocelot.Provider.Consul.Interfaces;
using Ocelot.Responses;
using System.Text;
diff --git a/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs b/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs
index c769f49c0..00c2715ee 100644
--- a/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs
+++ b/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs
@@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration;
using Ocelot.Logging;
+using Ocelot.Provider.Consul.Interfaces;
using Ocelot.ServiceDiscovery.Providers;
namespace Ocelot.Provider.Consul;
@@ -17,16 +18,20 @@ public static class ConsulProviderFactory
public static ServiceDiscoveryFinderDelegate Get { get; } = CreateProvider;
+ private static ConsulRegistryConfiguration configuration;
+ private static ConsulRegistryConfiguration ConfigurationGetter() => configuration;
+ public static Func GetConfiguration { get; } = ConfigurationGetter;
+
private static IServiceDiscoveryProvider CreateProvider(IServiceProvider provider,
ServiceProviderConfiguration config, DownstreamRoute route)
{
var factory = provider.GetService();
var consulFactory = provider.GetService();
- var consulRegistryConfiguration = new ConsulRegistryConfiguration(
- config.Scheme, config.Host, config.Port, route.ServiceName, config.Token);
+ configuration = new ConsulRegistryConfiguration(config.Scheme, config.Host, config.Port, route.ServiceName, config.Token);
+ var serviceBuilder = provider.GetService();
- var consulProvider = new Consul(consulRegistryConfiguration, factory, consulFactory);
+ var consulProvider = new Consul(configuration, factory, consulFactory, serviceBuilder);
if (PollConsul.Equals(config.Type, StringComparison.OrdinalIgnoreCase))
{
diff --git a/src/Ocelot.Provider.Consul/DefaultConsulServiceBuilder.cs b/src/Ocelot.Provider.Consul/DefaultConsulServiceBuilder.cs
new file mode 100644
index 000000000..7526bea65
--- /dev/null
+++ b/src/Ocelot.Provider.Consul/DefaultConsulServiceBuilder.cs
@@ -0,0 +1,103 @@
+using Ocelot.Infrastructure.Extensions;
+using Ocelot.Logging;
+using Ocelot.Provider.Consul.Interfaces;
+using Ocelot.Values;
+
+namespace Ocelot.Provider.Consul;
+
+public class DefaultConsulServiceBuilder : IConsulServiceBuilder
+{
+ private readonly ConsulRegistryConfiguration _configuration;
+ private readonly IConsulClient _client;
+ private readonly IOcelotLogger _logger;
+
+ public DefaultConsulServiceBuilder(
+ Func configurationFactory,
+ IConsulClientFactory clientFactory,
+ IOcelotLoggerFactory loggerFactory)
+ {
+ _configuration = configurationFactory.Invoke();
+ _client = clientFactory.Get(_configuration);
+ _logger = loggerFactory.CreateLogger();
+ }
+
+ public ConsulRegistryConfiguration Configuration => _configuration;
+ protected IConsulClient Client => _client;
+ protected IOcelotLogger Logger => _logger;
+
+ public virtual bool IsValid(ServiceEntry entry)
+ {
+ var service = entry.Service;
+ var address = service.Address;
+ bool valid = !string.IsNullOrEmpty(address)
+ && !address.StartsWith(Uri.UriSchemeHttp + "://", StringComparison.OrdinalIgnoreCase)
+ && !address.StartsWith(Uri.UriSchemeHttps + "://", StringComparison.OrdinalIgnoreCase)
+ && service.Port > 0;
+
+ if (!valid)
+ {
+ _logger.LogWarning(
+ () => $"Unable to use service address: '{service.Address}' and port: {service.Port} as it is invalid for the service: '{service.Service}'. Address must contain host only e.g. 'localhost', and port must be greater than 0.");
+ }
+
+ return valid;
+ }
+
+ public virtual IEnumerable BuildServices(ServiceEntry[] entries, Node[] nodes)
+ {
+ ArgumentNullException.ThrowIfNull(entries);
+ var services = new List(entries.Length);
+
+ foreach (var serviceEntry in entries)
+ {
+ if (IsValid(serviceEntry))
+ {
+ var serviceNode = GetNode(serviceEntry, nodes);
+ var item = CreateService(serviceEntry, serviceNode);
+ if (item != null)
+ {
+ services.Add(item);
+ }
+ }
+ }
+
+ return services;
+ }
+
+ protected virtual Node GetNode(ServiceEntry entry, Node[] nodes)
+ => entry?.Node ?? nodes?.FirstOrDefault(n => n.Address == entry?.Service?.Address);
+
+ public virtual Service CreateService(ServiceEntry entry, Node node)
+ => new(
+ GetServiceName(entry, node),
+ GetServiceHostAndPort(entry, node),
+ GetServiceId(entry, node),
+ GetServiceVersion(entry, node),
+ GetServiceTags(entry, node)
+ );
+
+ protected virtual string GetServiceName(ServiceEntry entry, Node node)
+ => entry.Service.Service;
+
+ protected virtual ServiceHostAndPort GetServiceHostAndPort(ServiceEntry entry, Node node)
+ => new(
+ GetDownstreamHost(entry, node),
+ entry.Service.Port);
+
+ protected virtual string GetDownstreamHost(ServiceEntry entry, Node node)
+ => node != null ? node.Name : entry.Service.Address;
+
+ protected virtual string GetServiceId(ServiceEntry entry, Node node)
+ => entry.Service.ID;
+
+ protected virtual string GetServiceVersion(ServiceEntry entry, Node node)
+ => entry.Service.Tags
+ ?.FirstOrDefault(tag => tag.StartsWith(VersionPrefix, StringComparison.Ordinal))
+ ?.TrimStart(VersionPrefix)
+ ?? string.Empty;
+
+ protected virtual IEnumerable GetServiceTags(ServiceEntry entry, Node node)
+ => entry.Service.Tags ?? Enumerable.Empty();
+
+ private const string VersionPrefix = "version-";
+}
diff --git a/src/Ocelot.Provider.Consul/IConsulClientFactory.cs b/src/Ocelot.Provider.Consul/Interfaces/IConsulClientFactory.cs
similarity index 68%
rename from src/Ocelot.Provider.Consul/IConsulClientFactory.cs
rename to src/Ocelot.Provider.Consul/Interfaces/IConsulClientFactory.cs
index 3ee3a2b25..0fe12aa08 100644
--- a/src/Ocelot.Provider.Consul/IConsulClientFactory.cs
+++ b/src/Ocelot.Provider.Consul/Interfaces/IConsulClientFactory.cs
@@ -1,4 +1,4 @@
-namespace Ocelot.Provider.Consul;
+namespace Ocelot.Provider.Consul.Interfaces;
public interface IConsulClientFactory
{
diff --git a/src/Ocelot.Provider.Consul/Interfaces/IConsulServiceBuilder.cs b/src/Ocelot.Provider.Consul/Interfaces/IConsulServiceBuilder.cs
new file mode 100644
index 000000000..0555b0144
--- /dev/null
+++ b/src/Ocelot.Provider.Consul/Interfaces/IConsulServiceBuilder.cs
@@ -0,0 +1,11 @@
+using Ocelot.Values;
+
+namespace Ocelot.Provider.Consul.Interfaces;
+
+public interface IConsulServiceBuilder
+{
+ ConsulRegistryConfiguration Configuration { get; }
+ bool IsValid(ServiceEntry entry);
+ IEnumerable BuildServices(ServiceEntry[] entries, Node[] nodes);
+ Service CreateService(ServiceEntry serviceEntry, Node serviceNode);
+}
diff --git a/src/Ocelot.Provider.Consul/OcelotBuilderExtensions.cs b/src/Ocelot.Provider.Consul/OcelotBuilderExtensions.cs
index dac7aecff..0c064f780 100644
--- a/src/Ocelot.Provider.Consul/OcelotBuilderExtensions.cs
+++ b/src/Ocelot.Provider.Consul/OcelotBuilderExtensions.cs
@@ -2,21 +2,57 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Ocelot.Configuration.Repository;
using Ocelot.DependencyInjection;
+using Ocelot.Provider.Consul.Interfaces;
namespace Ocelot.Provider.Consul;
public static class OcelotBuilderExtensions
{
+ ///
+ /// Integrates Consul service discovery into the DI, atop the existing Ocelot services.
+ ///
+ ///
+ /// Default services:
+ ///
+ /// - The service is an instance of .
+ /// - The service is an instance of .
+ ///
+ ///
+ /// The Ocelot Builder instance, default.
+ /// The reference to the same extended object.
public static IOcelotBuilder AddConsul(this IOcelotBuilder builder)
{
builder.Services
.AddSingleton(ConsulProviderFactory.Get)
+ .AddSingleton(ConsulProviderFactory.GetConfiguration)
.AddSingleton()
+ .AddSingleton()
.RemoveAll(typeof(IFileConfigurationPollerOptions))
.AddSingleton();
return builder;
}
+ ///
+ /// Integrates Consul service discovery into the DI, atop the existing Ocelot services, with service builder overriding.
+ ///
+ ///
+ /// Services to override:
+ ///
+ /// - The service has been substituted with a instance.
+ ///
+ ///
+ /// The service builder type.
+ /// The Ocelot Builder instance, default.
+ /// The reference to the same extended object.
+ public static IOcelotBuilder AddConsul(this IOcelotBuilder builder)
+ where TServiceBuilder : class, IConsulServiceBuilder
+ {
+ AddConsul(builder).Services
+ .RemoveAll()
+ .AddSingleton(typeof(IConsulServiceBuilder), typeof(TServiceBuilder));
+ return builder;
+ }
+
public static IOcelotBuilder AddConfigStoredInConsul(this IOcelotBuilder builder)
{
builder.Services
diff --git a/src/Ocelot.Provider.Kubernetes/EndPointClientV1.cs b/src/Ocelot.Provider.Kubernetes/EndPointClientV1.cs
index 22f58e538..83418957b 100644
--- a/src/Ocelot.Provider.Kubernetes/EndPointClientV1.cs
+++ b/src/Ocelot.Provider.Kubernetes/EndPointClientV1.cs
@@ -1,6 +1,7 @@
using HTTPlease;
using KubeClient.Models;
using KubeClient.ResourceClients;
+using Ocelot.Provider.Kubernetes.Interfaces;
namespace Ocelot.Provider.Kubernetes
{
diff --git a/src/Ocelot.Provider.Kubernetes/IEndPointClient.cs b/src/Ocelot.Provider.Kubernetes/Interfaces/IEndPointClient.cs
similarity index 83%
rename from src/Ocelot.Provider.Kubernetes/IEndPointClient.cs
rename to src/Ocelot.Provider.Kubernetes/Interfaces/IEndPointClient.cs
index 6dfca972d..10f79f8af 100644
--- a/src/Ocelot.Provider.Kubernetes/IEndPointClient.cs
+++ b/src/Ocelot.Provider.Kubernetes/Interfaces/IEndPointClient.cs
@@ -1,7 +1,7 @@
using KubeClient.Models;
using KubeClient.ResourceClients;
-namespace Ocelot.Provider.Kubernetes;
+namespace Ocelot.Provider.Kubernetes.Interfaces;
public interface IEndPointClient : IKubeResourceClient
{
diff --git a/src/Ocelot.Provider.Kubernetes/Interfaces/IKubeServiceBuilder.cs b/src/Ocelot.Provider.Kubernetes/Interfaces/IKubeServiceBuilder.cs
new file mode 100644
index 000000000..d5c6bcc30
--- /dev/null
+++ b/src/Ocelot.Provider.Kubernetes/Interfaces/IKubeServiceBuilder.cs
@@ -0,0 +1,9 @@
+using KubeClient.Models;
+using Ocelot.Values;
+
+namespace Ocelot.Provider.Kubernetes.Interfaces;
+
+public interface IKubeServiceBuilder
+{
+ IEnumerable BuildServices(KubeRegistryConfiguration configuration, EndpointsV1 endpoint);
+}
diff --git a/src/Ocelot.Provider.Kubernetes/Interfaces/IKubeServiceCreator.cs b/src/Ocelot.Provider.Kubernetes/Interfaces/IKubeServiceCreator.cs
new file mode 100644
index 000000000..a6ace7b2d
--- /dev/null
+++ b/src/Ocelot.Provider.Kubernetes/Interfaces/IKubeServiceCreator.cs
@@ -0,0 +1,10 @@
+using KubeClient.Models;
+using Ocelot.Values;
+
+namespace Ocelot.Provider.Kubernetes.Interfaces;
+
+public interface IKubeServiceCreator
+{
+ IEnumerable Create(KubeRegistryConfiguration configuration, EndpointsV1 endpoint, EndpointSubsetV1 subset);
+ IEnumerable CreateInstance(KubeRegistryConfiguration configuration, EndpointsV1 endpoint, EndpointSubsetV1 subset, EndpointAddressV1 address);
+}
diff --git a/src/Ocelot.Provider.Kubernetes/Kube.cs b/src/Ocelot.Provider.Kubernetes/Kube.cs
index 15b5cf6cc..5350f43b9 100644
--- a/src/Ocelot.Provider.Kubernetes/Kube.cs
+++ b/src/Ocelot.Provider.Kubernetes/Kube.cs
@@ -1,5 +1,6 @@
using KubeClient.Models;
using Ocelot.Logging;
+using Ocelot.Provider.Kubernetes.Interfaces;
using Ocelot.Values;
namespace Ocelot.Provider.Kubernetes;
@@ -9,47 +10,44 @@ namespace Ocelot.Provider.Kubernetes;
///
public class Kube : IServiceDiscoveryProvider
{
- private readonly KubeRegistryConfiguration _kubeRegistryConfiguration;
+ private readonly KubeRegistryConfiguration _configuration;
private readonly IOcelotLogger _logger;
private readonly IKubeApiClient _kubeApi;
-
- public Kube(KubeRegistryConfiguration kubeRegistryConfiguration, IOcelotLoggerFactory factory, IKubeApiClient kubeApi)
+ private readonly IKubeServiceBuilder _serviceBuilder;
+ private readonly List _services;
+
+ public Kube(
+ KubeRegistryConfiguration configuration,
+ IOcelotLoggerFactory factory,
+ IKubeApiClient kubeApi,
+ IKubeServiceBuilder serviceBuilder)
{
- _kubeRegistryConfiguration = kubeRegistryConfiguration;
+ _configuration = configuration;
_logger = factory.CreateLogger();
_kubeApi = kubeApi;
+ _serviceBuilder = serviceBuilder;
+ _services = new();
}
- public async Task> GetAsync()
+ public virtual async Task> GetAsync()
{
var endpoint = await _kubeApi
.ResourceClient(client => new EndPointClientV1(client))
- .GetAsync(_kubeRegistryConfiguration.KeyOfServiceInK8s, _kubeRegistryConfiguration.KubeNamespace);
+ .GetAsync(_configuration.KeyOfServiceInK8s, _configuration.KubeNamespace);
- var services = new List();
- if (endpoint != null && endpoint.Subsets.Any())
+ _services.Clear();
+ if (endpoint?.Subsets.Count != 0)
{
- services.AddRange(BuildServices(endpoint));
+ _services.AddRange(BuildServices(_configuration, endpoint));
}
else
{
- _logger.LogWarning(() => $"namespace:{_kubeRegistryConfiguration.KubeNamespace}service:{_kubeRegistryConfiguration.KeyOfServiceInK8s} Unable to use ,it is invalid. Address must contain host only e.g. localhost and port must be greater than 0");
+ _logger.LogWarning(() => $"K8s Namespace:{_configuration.KubeNamespace}, Service:{_configuration.KeyOfServiceInK8s}; Unable to use: it is invalid. Address must contain host only e.g. localhost and port must be greater than 0!");
}
- return services;
+ return _services;
}
- private static List BuildServices(EndpointsV1 endpoint)
- {
- var services = new List();
-
- foreach (var subset in endpoint.Subsets)
- {
- services.AddRange(subset.Addresses.Select(address => new Service(endpoint.Metadata.Name,
- new ServiceHostAndPort(address.Ip, subset.Ports.First().Port),
- endpoint.Metadata.Uid, string.Empty, Enumerable.Empty())));
- }
-
- return services;
- }
+ protected virtual IEnumerable BuildServices(KubeRegistryConfiguration configuration, EndpointsV1 endpoint)
+ => _serviceBuilder.BuildServices(configuration, endpoint);
}
diff --git a/src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs b/src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs
index 2a3d7e815..b264e1b67 100644
--- a/src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs
+++ b/src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs
@@ -1,8 +1,8 @@
-namespace Ocelot.Provider.Kubernetes
+namespace Ocelot.Provider.Kubernetes;
+
+public class KubeRegistryConfiguration
{
- public class KubeRegistryConfiguration
- {
- public string KubeNamespace { get; set; }
- public string KeyOfServiceInK8s { get; set; }
- }
+ public string KubeNamespace { get; set; }
+ public string KeyOfServiceInK8s { get; set; }
+ public string Scheme { get; set; }
}
diff --git a/src/Ocelot.Provider.Kubernetes/KubeServiceBuilder.cs b/src/Ocelot.Provider.Kubernetes/KubeServiceBuilder.cs
new file mode 100644
index 000000000..589cfe5ba
--- /dev/null
+++ b/src/Ocelot.Provider.Kubernetes/KubeServiceBuilder.cs
@@ -0,0 +1,36 @@
+using KubeClient.Models;
+using Ocelot.Logging;
+using Ocelot.Provider.Kubernetes.Interfaces;
+using Ocelot.Values;
+
+namespace Ocelot.Provider.Kubernetes;
+
+public class KubeServiceBuilder : IKubeServiceBuilder
+{
+ private readonly IOcelotLogger _logger;
+ private readonly IKubeServiceCreator _serviceCreator;
+
+ public KubeServiceBuilder(IOcelotLoggerFactory factory, IKubeServiceCreator serviceCreator)
+ {
+ ArgumentNullException.ThrowIfNull(factory);
+ _logger = factory.CreateLogger();
+
+ ArgumentNullException.ThrowIfNull(serviceCreator);
+ _serviceCreator = serviceCreator;
+ }
+
+ public virtual IEnumerable BuildServices(KubeRegistryConfiguration configuration, EndpointsV1 endpoint)
+ {
+ ArgumentNullException.ThrowIfNull(configuration);
+ ArgumentNullException.ThrowIfNull(endpoint);
+
+ var services = endpoint.Subsets
+ .SelectMany(subset => _serviceCreator.Create(configuration, endpoint, subset))
+ .ToArray();
+
+ _logger.LogDebug(() => $"K8s '{Check(endpoint.Kind)}:{Check(endpoint.ApiVersion)}:{Check(endpoint.Metadata?.Name)}' endpoint: Total built {services.Length} services.");
+ return services;
+ }
+
+ private static string Check(string str) => string.IsNullOrEmpty(str) ? "?" : str;
+}
diff --git a/src/Ocelot.Provider.Kubernetes/KubeServiceCreator.cs b/src/Ocelot.Provider.Kubernetes/KubeServiceCreator.cs
new file mode 100644
index 000000000..3d51159c3
--- /dev/null
+++ b/src/Ocelot.Provider.Kubernetes/KubeServiceCreator.cs
@@ -0,0 +1,59 @@
+using KubeClient.Models;
+using Ocelot.Logging;
+using Ocelot.Provider.Kubernetes.Interfaces;
+using Ocelot.Values;
+
+namespace Ocelot.Provider.Kubernetes;
+
+public class KubeServiceCreator : IKubeServiceCreator
+{
+ private readonly IOcelotLogger _logger;
+
+ public KubeServiceCreator(IOcelotLoggerFactory factory)
+ {
+ ArgumentNullException.ThrowIfNull(factory);
+ _logger = factory.CreateLogger();
+ }
+
+ public virtual IEnumerable Create(KubeRegistryConfiguration configuration, EndpointsV1 endpoint, EndpointSubsetV1 subset)
+ => (configuration == null || endpoint == null || subset == null)
+ ? Array.Empty()
+ : subset.Addresses
+ .SelectMany(address => CreateInstance(configuration, endpoint, subset, address))
+ .ToArray();
+
+ public virtual IEnumerable CreateInstance(KubeRegistryConfiguration configuration, EndpointsV1 endpoint, EndpointSubsetV1 subset, EndpointAddressV1 address)
+ {
+ var instance = new Service(
+ GetServiceName(configuration, endpoint, subset, address),
+ GetServiceHostAndPort(configuration, endpoint, subset, address),
+ GetServiceId(configuration, endpoint, subset, address),
+ GetServiceVersion(configuration, endpoint, subset, address),
+ GetServiceTags(configuration, endpoint, subset, address)
+ );
+ return new Service[] { instance };
+ }
+
+ protected virtual string GetServiceName(KubeRegistryConfiguration configuration, EndpointsV1 endpoint, EndpointSubsetV1 subset, EndpointAddressV1 address)
+ => endpoint.Metadata?.Name;
+
+ protected virtual ServiceHostAndPort GetServiceHostAndPort(KubeRegistryConfiguration configuration, EndpointsV1 endpoint, EndpointSubsetV1 subset, EndpointAddressV1 address)
+ {
+ var ports = subset.Ports;
+ bool portNameToScheme(EndpointPortV1 p) => string.Equals(p.Name, configuration.Scheme, StringComparison.InvariantCultureIgnoreCase);
+ var portV1 = string.IsNullOrEmpty(configuration.Scheme) || !ports.Any(portNameToScheme)
+ ? ports.FirstOrDefault()
+ : ports.FirstOrDefault(portNameToScheme);
+ portV1 ??= new();
+ portV1.Name ??= configuration.Scheme ?? string.Empty;
+ _logger.LogDebug(() => $"K8s service with key '{configuration.KeyOfServiceInK8s}' and address {address.Ip}; Detected port is {portV1.Name}:{portV1.Port}. Total {ports.Count} ports of [{string.Join(',', ports.Select(p => p.Name))}].");
+ return new ServiceHostAndPort(address.Ip, portV1.Port, portV1.Name);
+ }
+
+ protected virtual string GetServiceId(KubeRegistryConfiguration configuration, EndpointsV1 endpoint, EndpointSubsetV1 subset, EndpointAddressV1 address)
+ => endpoint.Metadata?.Uid;
+ protected virtual string GetServiceVersion(KubeRegistryConfiguration configuration, EndpointsV1 endpoint, EndpointSubsetV1 subset, EndpointAddressV1 address)
+ => endpoint.ApiVersion;
+ protected virtual IEnumerable GetServiceTags(KubeRegistryConfiguration configuration, EndpointsV1 endpoint, EndpointSubsetV1 subset, EndpointAddressV1 address)
+ => Enumerable.Empty();
+}
diff --git a/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs b/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs
index 4507c03e6..a3a1d48c0 100644
--- a/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs
+++ b/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs
@@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration;
-using Ocelot.Logging;
+using Ocelot.Logging;
+using Ocelot.Provider.Kubernetes.Interfaces;
namespace Ocelot.Provider.Kubernetes
{
@@ -17,14 +18,16 @@ private static IServiceDiscoveryProvider CreateProvider(IServiceProvider provide
{
var factory = provider.GetService();
var kubeClient = provider.GetService();
+ var serviceBuilder = provider.GetService();
var configuration = new KubeRegistryConfiguration
{
KeyOfServiceInK8s = route.ServiceName,
KubeNamespace = string.IsNullOrEmpty(route.ServiceNamespace) ? config.Namespace : route.ServiceNamespace,
+ Scheme = route.DownstreamScheme,
};
- var defaultK8sProvider = new Kube(configuration, factory, kubeClient);
+ var defaultK8sProvider = new Kube(configuration, factory, kubeClient, serviceBuilder);
return PollKube.Equals(config.Type, StringComparison.OrdinalIgnoreCase)
? new PollKube(config.PollingInterval, factory, defaultK8sProvider)
diff --git a/src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs b/src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs
index 0110bddbe..fadedc356 100644
--- a/src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs
+++ b/src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs
@@ -1,16 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
+using Ocelot.Provider.Kubernetes.Interfaces;
-namespace Ocelot.Provider.Kubernetes
+namespace Ocelot.Provider.Kubernetes;
+
+public static class OcelotBuilderExtensions
{
- public static class OcelotBuilderExtensions
+ public static IOcelotBuilder AddKubernetes(this IOcelotBuilder builder, bool usePodServiceAccount = true)
{
- public static IOcelotBuilder AddKubernetes(this IOcelotBuilder builder, bool usePodServiceAccount = true)
- {
- builder.Services
- .AddSingleton(KubernetesProviderFactory.Get)
- .AddKubeClient(usePodServiceAccount);
- return builder;
- }
+ builder.Services
+ .AddKubeClient(usePodServiceAccount)
+ .AddSingleton(KubernetesProviderFactory.Get)
+ .AddSingleton()
+ .AddSingleton();
+ return builder;
}
}
diff --git a/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj b/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj
index 176e4769f..0d9c84e67 100644
--- a/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj
+++ b/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj
@@ -34,7 +34,7 @@
all
-
+
diff --git a/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs b/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs
index bfa26cf50..76fcc35d5 100644
--- a/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs
+++ b/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs
@@ -6,7 +6,6 @@
using Ocelot.Errors.QoS;
using Ocelot.Logging;
using Ocelot.Provider.Polly.Interfaces;
-using Ocelot.Provider.Polly.v7;
using Ocelot.Requester;
using Polly.CircuitBreaker;
using Polly.Registry;
@@ -32,7 +31,7 @@ public static class OcelotBuilderExtensions
///
/// Adds Polly QoS provider to Ocelot by custom delegate and with custom error mapping.
///
- /// QoS provider to use (by default use ).
+ /// QoS provider to use (by default use ).
/// Ocelot builder to extend.
/// Your customized delegating handler (to manage QoS behavior by yourself).
/// Your customized error mapping.
@@ -94,7 +93,7 @@ public static IOcelotBuilder AddPolly(this IOcelotBuilder builder)
/// Defaults:
///
///
- ///
+ ///
///
///
///
@@ -112,96 +111,4 @@ public static IOcelotBuilder AddPolly(this IOcelotBuilder builder)
/// A object, but concrete type is the class.
private static DelegatingHandler GetDelegatingHandler(DownstreamRoute route, IHttpContextAccessor contextAccessor, IOcelotLoggerFactory loggerFactory)
=> new PollyResiliencePipelineDelegatingHandler(route, contextAccessor, loggerFactory);
-
- #region Obsolete extensions will be removed in future version
-
- ///
- /// Adds Polly QoS provider to Ocelot by custom delegate and with custom error mapping.
- ///
- /// QoS provider to use (by default use ).
- /// Ocelot builder to extend.
- /// Your customized delegating handler (to manage QoS behavior by yourself).
- /// Your customized error mapping.
- /// The reference to the same extended object.
- [Obsolete("Use AddPolly instead, it will be remove in future version")]
- public static IOcelotBuilder AddPollyV7(this IOcelotBuilder builder, QosDelegatingHandlerDelegate delegatingHandler, IDictionary> errorMapping)
- where TProvider : class, IPollyQoSProvider
- {
- builder.Services
- .AddSingleton(errorMapping)
- .AddSingleton, TProvider>()
- .AddSingleton(delegatingHandler);
- return builder;
- }
-
- ///
- /// Adds Polly QoS provider to Ocelot with custom error mapping, but default is used.
- ///
- /// QoS provider to use (by default use ).
- /// Ocelot builder to extend.
- /// Your customized error mapping.
- /// The reference to the same extended object.
- [Obsolete("Use AddPolly instead, it will be remove in future version")]
- public static IOcelotBuilder AddPollyV7(this IOcelotBuilder builder, IDictionary> errorMapping)
- where TProvider : class, IPollyQoSProvider
- => AddPollyV7(builder, GetDelegatingHandlerV7, errorMapping);
-
- ///
- /// Adds Polly QoS provider to Ocelot with custom delegate, but default error mapping is used.
- ///
- /// QoS provider to use (by default use ).
- /// Ocelot builder to extend.
- /// Your customized delegating handler (to manage QoS behavior by yourself).
- /// The reference to the same extended object.
- [Obsolete("Use AddPolly instead, it will be remove in future version")]
- public static IOcelotBuilder AddPollyV7(this IOcelotBuilder builder, QosDelegatingHandlerDelegate delegatingHandler)
- where TProvider : class, IPollyQoSProvider
- => AddPollyV7(builder, delegatingHandler, DefaultErrorMapping);
-
- ///
- /// Adds Polly QoS provider to Ocelot by defaults.
- ///
- ///
- /// Defaults:
- ///
- ///
- ///
- ///
- ///
- /// QoS provider to use (by default use ).
- /// Ocelot builder to extend.
- /// The reference to the same extended object.
- [Obsolete("Use AddPolly instead, it will be remove in future version")]
- public static IOcelotBuilder AddPollyV7(this IOcelotBuilder builder)
- where TProvider : class, IPollyQoSProvider
- => AddPollyV7(builder, GetDelegatingHandlerV7, DefaultErrorMapping);
-
- ///
- /// Adds Polly QoS provider to Ocelot by defaults with default QoS provider.
- ///
- ///
- /// Defaults:
- ///
- ///
- ///
- ///
- ///
- ///
- /// Ocelot builder to extend.
- /// The reference to the same extended object.
- [Obsolete("Use AddPolly instead, it will be remove in future version")]
- public static IOcelotBuilder AddPollyV7(this IOcelotBuilder builder)
- => AddPollyV7(builder, GetDelegatingHandlerV7, DefaultErrorMapping);
-
- ///
- /// Creates default delegating handler based on the type.
- ///
- /// The downstream route to apply the handler for.
- /// The context accessor of the route.
- /// The factory of logger.
- /// A object, but concrete type is the class.
- private static DelegatingHandler GetDelegatingHandlerV7(DownstreamRoute route, IHttpContextAccessor contextAccessor, IOcelotLoggerFactory loggerFactory)
- => new PollyPoliciesDelegatingHandler(route, contextAccessor, loggerFactory);
-
- #endregion
}
diff --git a/src/Ocelot.Provider.Polly/PollyQoSProviderBase.cs b/src/Ocelot.Provider.Polly/PollyQoSProviderBase.cs
deleted file mode 100644
index 31ca23451..000000000
--- a/src/Ocelot.Provider.Polly/PollyQoSProviderBase.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Ocelot.Configuration;
-using System.Net;
-
-namespace Ocelot.Provider.Polly;
-
-public abstract class PollyQoSProviderBase
-{
- protected static readonly HashSet ServerErrorCodes =
- [
- HttpStatusCode.InternalServerError,
- HttpStatusCode.NotImplemented,
- HttpStatusCode.BadGateway,
- HttpStatusCode.ServiceUnavailable,
- HttpStatusCode.GatewayTimeout,
- HttpStatusCode.HttpVersionNotSupported,
- HttpStatusCode.VariantAlsoNegotiates,
- HttpStatusCode.InsufficientStorage,
- HttpStatusCode.LoopDetected,
- ];
-
- protected static string GetRouteName(DownstreamRoute route)
- => string.IsNullOrWhiteSpace(route.ServiceName)
- ? route.UpstreamPathTemplate?.Template ?? route.DownstreamPathTemplate?.Value ?? string.Empty
- : route.ServiceName;
-}
diff --git a/src/Ocelot.Provider.Polly/PollyQoSResiliencePipelineProvider.cs b/src/Ocelot.Provider.Polly/PollyQoSResiliencePipelineProvider.cs
index be2df16b3..a0000c62d 100644
--- a/src/Ocelot.Provider.Polly/PollyQoSResiliencePipelineProvider.cs
+++ b/src/Ocelot.Provider.Polly/PollyQoSResiliencePipelineProvider.cs
@@ -4,24 +4,46 @@
using Polly.CircuitBreaker;
using Polly.Registry;
using Polly.Timeout;
+using System.Net;
namespace Ocelot.Provider.Polly;
///
/// Default provider for Polly V8 pipelines.
///
-public class PollyQoSResiliencePipelineProvider : PollyQoSProviderBase, IPollyQoSResiliencePipelineProvider
+public class PollyQoSResiliencePipelineProvider : IPollyQoSResiliencePipelineProvider
{
- private readonly ResiliencePipelineRegistry _resiliencePipelineRegistry;
+ private readonly ResiliencePipelineRegistry _registry;
private readonly IOcelotLogger _logger;
- public PollyQoSResiliencePipelineProvider(IOcelotLoggerFactory loggerFactory,
- ResiliencePipelineRegistry resiliencePipelineRegistry)
+ public PollyQoSResiliencePipelineProvider(
+ IOcelotLoggerFactory loggerFactory,
+ ResiliencePipelineRegistry registry)
{
- _resiliencePipelineRegistry = resiliencePipelineRegistry;
_logger = loggerFactory.CreateLogger();
+ _registry = registry;
}
+ protected static readonly HashSet DefaultServerErrorCodes = new()
+ {
+ HttpStatusCode.InternalServerError,
+ HttpStatusCode.NotImplemented,
+ HttpStatusCode.BadGateway,
+ HttpStatusCode.ServiceUnavailable,
+ HttpStatusCode.GatewayTimeout,
+ HttpStatusCode.HttpVersionNotSupported,
+ HttpStatusCode.VariantAlsoNegotiates,
+ HttpStatusCode.InsufficientStorage,
+ HttpStatusCode.LoopDetected,
+ };
+
+ protected virtual HashSet ServerErrorCodes { get; } = DefaultServerErrorCodes;
+
+ protected virtual string GetRouteName(DownstreamRoute route)
+ => string.IsNullOrWhiteSpace(route.ServiceName)
+ ? route.UpstreamPathTemplate?.Template ?? route.DownstreamPathTemplate?.Value ?? string.Empty
+ : route.ServiceName;
+
///
/// Gets Polly V8 resilience pipeline (applies QoS feature) for the route.
///
@@ -29,56 +51,86 @@ public PollyQoSResiliencePipelineProvider(IOcelotLoggerFactory loggerFactory,
/// A object where T is .
public ResiliencePipeline GetResiliencePipeline(DownstreamRoute route)
{
- var options = route.QosOptions;
-
- // Check if we need pipeline at all before calling GetOrAddPipeline
- if (options is null ||
- (options.ExceptionsAllowedBeforeBreaking == 0 && options.TimeoutValue is int.MaxValue))
+ var options = route?.QosOptions;
+ if (options is null || !options.UseQos)
{
- return null; // shortcut > no qos
+ return ResiliencePipeline.Empty; // shortcut -> No QoS
}
var currentRouteName = GetRouteName(route);
- return _resiliencePipelineRegistry.GetOrAddPipeline(
- key: new OcelotResiliencePipelineKey(currentRouteName),
- configure: (builder) => PollyResiliencePipelineWrapperFactory(builder, route));
+ return _registry.GetOrAddPipeline(
+ key: new OcelotResiliencePipelineKey(currentRouteName),
+ configure: (builder) => ConfigureStrategies(builder, route));
}
- private void PollyResiliencePipelineWrapperFactory(ResiliencePipelineBuilder builder, DownstreamRoute route)
+ protected virtual void ConfigureStrategies(ResiliencePipelineBuilder builder, DownstreamRoute route)
{
- var options = route.QosOptions;
-
- // Add TimeoutStrategy if TimeoutValue is not int.MaxValue and greater than 0
- if (options.TimeoutValue != int.MaxValue && options.TimeoutValue > 0)
- {
- builder.AddTimeout(TimeSpan.FromMilliseconds(options.TimeoutValue));
- }
+ ConfigureCircuitBreaker(builder, route);
+ ConfigureTimeout(builder, route);
+ }
- // Add CircuitBreakerStrategy only if ExceptionsAllowedBeforeBreaking is greater than 0
- if (options.ExceptionsAllowedBeforeBreaking <= 0)
+ protected virtual ResiliencePipelineBuilder ConfigureCircuitBreaker(ResiliencePipelineBuilder builder, DownstreamRoute route)
+ {
+ // Add CircuitBreaker strategy only if ExceptionsAllowedBeforeBreaking is greater/equal than/to 2
+ if (route.QosOptions.ExceptionsAllowedBeforeBreaking < 2)
{
- return; // shortcut > no qos (no timeout, no ExceptionsAllowedBeforeBreaking)
+ return builder;
}
+ var options = route.QosOptions;
var info = $"Circuit Breaker for Route: {GetRouteName(route)}: ";
-
- var circuitBreakerStrategyOptions = new CircuitBreakerStrategyOptions
+ var strategyOptions = new CircuitBreakerStrategyOptions
{
FailureRatio = 0.8,
SamplingDuration = TimeSpan.FromSeconds(10),
- MinimumThroughput = options.ExceptionsAllowedBeforeBreaking,
- BreakDuration = TimeSpan.FromMilliseconds(options.DurationOfBreak),
+ MinimumThroughput = options.ExceptionsAllowedBeforeBreaking,
+ BreakDuration = options.DurationOfBreak > QoSOptions.LowBreakDuration
+ ? TimeSpan.FromMilliseconds(options.DurationOfBreak)
+ : TimeSpan.FromMilliseconds(QoSOptions.DefaultBreakDuration),
ShouldHandle = new PredicateBuilder()
.HandleResult(message => ServerErrorCodes.Contains(message.StatusCode))
.Handle()
.Handle(),
OnOpened = args =>
{
- _logger.LogError(info + $"Breaking for {args.BreakDuration.TotalMilliseconds} ms", args.Outcome.Exception);
+ _logger.LogError(info + $"Breaking for {args.BreakDuration.TotalMilliseconds} ms",
+ args.Outcome.Exception);
+ return ValueTask.CompletedTask;
+ },
+ OnClosed = _ =>
+ {
+ _logger.LogInformation(info + "Closed");
+ return ValueTask.CompletedTask;
+ },
+ OnHalfOpened = _ =>
+ {
+ _logger.LogInformation(info + "Half Opened");
return ValueTask.CompletedTask;
},
};
+ return builder.AddCircuitBreaker(strategyOptions);
+ }
+
+ protected virtual ResiliencePipelineBuilder ConfigureTimeout(ResiliencePipelineBuilder builder, DownstreamRoute route)
+ {
+ var options = route.QosOptions;
- builder.AddCircuitBreaker(circuitBreakerStrategyOptions);
+ // Add Timeout strategy if TimeoutValue is not int.MaxValue and greater than 0
+ // TimeoutValue must be defined in QosOptions!
+ if (options.TimeoutValue == int.MaxValue || options.TimeoutValue <= 0)
+ {
+ return builder;
+ }
+
+ var strategyOptions = new TimeoutStrategyOptions
+ {
+ Timeout = TimeSpan.FromMilliseconds(options.TimeoutValue),
+ OnTimeout = _ =>
+ {
+ _logger.LogInformation($"Timeout for Route: {GetRouteName(route)}");
+ return ValueTask.CompletedTask;
+ },
+ };
+ return builder.AddTimeout(strategyOptions);
}
}
diff --git a/src/Ocelot.Provider.Polly/Usings.cs b/src/Ocelot.Provider.Polly/Usings.cs
index fb5c12dc6..be2de9d09 100644
--- a/src/Ocelot.Provider.Polly/Usings.cs
+++ b/src/Ocelot.Provider.Polly/Usings.cs
@@ -1,10 +1,8 @@
// Default Microsoft.NET.Sdk namespaces
+// Project extra global namespaces
+global using Polly;
global using System;
global using System.Collections.Generic;
-global using System.Linq;
global using System.Net.Http;
global using System.Threading;
global using System.Threading.Tasks;
-
-// Project extra global namespaces
-global using Polly;
diff --git a/src/Ocelot.Provider.Polly/v7/IPollyQoSProvider.cs b/src/Ocelot.Provider.Polly/v7/IPollyQoSProvider.cs
deleted file mode 100644
index 1537439b9..000000000
--- a/src/Ocelot.Provider.Polly/v7/IPollyQoSProvider.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Ocelot.Configuration;
-
-namespace Ocelot.Provider.Polly.v7;
-
-[Obsolete("It is obsolete because now, we use IPollyQoSResiliencePipelineProvider with new v8 resilience strategies")]
-public interface IPollyQoSProvider
- where TResult : class
-{
- PollyPolicyWrapper GetPollyPolicyWrapper(DownstreamRoute route);
-}
diff --git a/src/Ocelot.Provider.Polly/v7/OcelotBuilderExtensions.cs b/src/Ocelot.Provider.Polly/v7/OcelotBuilderExtensions.cs
deleted file mode 100644
index 6098bbe01..000000000
--- a/src/Ocelot.Provider.Polly/v7/OcelotBuilderExtensions.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.DependencyInjection;
-using Ocelot.Configuration;
-using Ocelot.DependencyInjection;
-using Ocelot.Errors;
-using Ocelot.Errors.QoS;
-using Ocelot.Logging;
-using Ocelot.Requester;
-using Polly.CircuitBreaker;
-using Polly.Timeout;
-
-namespace Ocelot.Provider.Polly.v7;
-
-public static class OcelotBuilderExtensions
-{
- ///
- /// Default mapping of Polly s to objects.
- ///
- public static readonly IDictionary> DefaultErrorMapping = new Dictionary>
- {
- {typeof(TaskCanceledException), CreateRequestTimedOutError},
- {typeof(TimeoutRejectedException), CreateRequestTimedOutError},
- {typeof(BrokenCircuitException), CreateRequestTimedOutError},
- {typeof(BrokenCircuitException), CreateRequestTimedOutError},
- };
-
- private static Error CreateRequestTimedOutError(Exception e) => new RequestTimedOutError(e);
-
- #region Obsolete extensions will be removed in future version
-
- ///
- /// Adds Polly QoS provider to Ocelot by custom delegate and with custom error mapping.
- ///
- /// QoS provider to use (by default use ).
- /// Ocelot builder to extend.
- /// Your customized delegating handler (to manage QoS behavior by yourself).
- /// Your customized error mapping.
- /// The reference to the same extended object.
- [Obsolete("We advise you to use Ocelot.Provider.Polly.AddPolly rather than this one. Polly v7 support will be removed in a future version")]
- public static IOcelotBuilder AddPollyV7(this IOcelotBuilder builder, QosDelegatingHandlerDelegate delegatingHandler, IDictionary> errorMapping)
- where TProvider : class, IPollyQoSProvider
- {
- builder.Services
- .AddSingleton(errorMapping)
- .AddSingleton, TProvider>()
- .AddSingleton(delegatingHandler);
- return builder;
- }
-
- ///
- /// Adds Polly QoS provider to Ocelot with custom error mapping, but default is used.
- ///
- /// QoS provider to use (by default use ).
- /// Ocelot builder to extend.
- /// Your customized error mapping.
- /// The reference to the same extended object.
- [Obsolete("We advise you to use Ocelot.Provider.Polly.AddPolly rather than this one. Polly v7 support will be removed in a future version")]
- public static IOcelotBuilder AddPollyV7(this IOcelotBuilder builder, IDictionary> errorMapping)
- where TProvider : class, IPollyQoSProvider
- => AddPollyV7(builder, GetDelegatingHandlerV7, errorMapping);
-
- ///
- /// Adds Polly QoS provider to Ocelot with custom delegate, but default error mapping is used.
- ///
- /// QoS provider to use (by default use ).
- /// Ocelot builder to extend.
- /// Your customized delegating handler (to manage QoS behavior by yourself).
- /// The reference to the same extended object.
- [Obsolete("We advise you to use Ocelot.Provider.Polly.AddPolly rather than this one. Polly v7 support will be removed in a future version")]
- public static IOcelotBuilder AddPollyV7(this IOcelotBuilder builder, QosDelegatingHandlerDelegate delegatingHandler)
- where TProvider : class, IPollyQoSProvider
- => AddPollyV7(builder, delegatingHandler, DefaultErrorMapping);
-
- ///
- /// Adds Polly QoS provider to Ocelot by defaults.
- ///
- ///
- /// Defaults:
- ///
- ///
- ///
- ///
- ///
- /// QoS provider to use (by default use ).
- /// Ocelot builder to extend.
- /// The reference to the same extended object.
- [Obsolete("Use AddPolly instead, it will be remove in future version")]
- public static IOcelotBuilder AddPollyV7(this IOcelotBuilder builder)
- where TProvider : class, IPollyQoSProvider
- => AddPollyV7(builder, GetDelegatingHandlerV7, DefaultErrorMapping);
-
- ///
- /// Adds Polly QoS provider to Ocelot by defaults with default QoS provider.
- ///
- ///
- /// Defaults:
- ///
- ///
- ///
- ///
- ///
- ///
- /// Ocelot builder to extend.
- /// The reference to the same extended object.
- [Obsolete("We advise you to use Ocelot.Provider.Polly.AddPolly rather than this one. Polly v7 support will be removed in a future version")]
- public static IOcelotBuilder AddPollyV7(this IOcelotBuilder builder)
- => AddPollyV7(builder, GetDelegatingHandlerV7, DefaultErrorMapping);
-
- ///
- /// Creates default delegating handler based on the type.
- ///
- /// The downstream route to apply the handler for.
- /// The context accessor of the route.
- /// The factory of logger.
- /// A object, but concrete type is the class.
- private static DelegatingHandler GetDelegatingHandlerV7(DownstreamRoute route, IHttpContextAccessor contextAccessor, IOcelotLoggerFactory loggerFactory)
- => new PollyPoliciesDelegatingHandler(route, contextAccessor, loggerFactory);
-
- #endregion
-}
diff --git a/src/Ocelot.Provider.Polly/v7/PollyPoliciesDelegatingHandler.cs b/src/Ocelot.Provider.Polly/v7/PollyPoliciesDelegatingHandler.cs
deleted file mode 100644
index 162e12337..000000000
--- a/src/Ocelot.Provider.Polly/v7/PollyPoliciesDelegatingHandler.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System.Diagnostics;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.DependencyInjection;
-using Ocelot.Configuration;
-using Ocelot.Logging;
-using Polly.CircuitBreaker;
-
-namespace Ocelot.Provider.Polly.v7;
-
-/// Delegates sending to downstream.
-/// Outdated V7 design! Use the class.
-[Obsolete("Due to new v8 policy definition in Polly 8 (use PollyResiliencePipelineDelegatingHandler)")]
-public class PollyPoliciesDelegatingHandler : DelegatingHandler
-{
- private readonly DownstreamRoute _route;
- private readonly IHttpContextAccessor _contextAccessor;
- private readonly IOcelotLogger _logger;
-
- public PollyPoliciesDelegatingHandler(
- DownstreamRoute route,
- IHttpContextAccessor contextAccessor,
- IOcelotLoggerFactory loggerFactory)
- {
- _route = route;
- _contextAccessor = contextAccessor;
- _logger = loggerFactory.CreateLogger();
- }
-
- private IPollyQoSProvider GetQoSProvider()
- {
- Debug.Assert(_contextAccessor.HttpContext != null, "_contextAccessor.HttpContext != null");
- return _contextAccessor.HttpContext.RequestServices.GetService>();
- }
-
- ///
- /// Sends an HTTP request to the inner handler to send to the server as an asynchronous operation.
- ///
- /// Downstream request.
- /// Token to cancel the task.
- /// A object of a result.
- /// Exception thrown when a circuit is broken.
- /// Exception thrown by and classes.
- protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- var qoSProvider = GetQoSProvider();
-
- // At least one policy (timeout) will be returned
- // AsyncPollyPolicy can't be null
- // AsyncPollyPolicy constructor will throw if no policy is provided
- var policy = qoSProvider.GetPollyPolicyWrapper(_route).AsyncPollyPolicy;
-
- return await policy.ExecuteAsync(async () => await base.SendAsync(request, cancellationToken));
- }
-}
diff --git a/src/Ocelot.Provider.Polly/v7/PollyPolicyWrapper.cs b/src/Ocelot.Provider.Polly/v7/PollyPolicyWrapper.cs
deleted file mode 100644
index 821743433..000000000
--- a/src/Ocelot.Provider.Polly/v7/PollyPolicyWrapper.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace Ocelot.Provider.Polly.v7;
-
-public class PollyPolicyWrapper
- where TResult : class
-{
- ///