From ae73e48a702f699e352a9eb70c950ac9956ab95a Mon Sep 17 00:00:00 2001
From: Copilot <223556219+Copilot@users.noreply.github.com>
Date: Sat, 13 Jun 2026 19:36:03 +0300
Subject: [PATCH 01/22] Scaffold System.ServiceModel.Web package
Add empty System.ServiceModel.Web package targeting $(DotNetVersion).
Mirrors the System.ServiceModel.NetNamedPipe template:
- Single TFM, no ref/ folder, two-entry solution wiring.
- System.SR resource class via Resources/Strings.resx auto-generation.
- ProjectReference to System.ServiceModel.Primitives and System.ServiceModel.Http.
Add InternalsVisibleTo("System.ServiceModel.Web") to
System.ServiceModel.Primitives so the upcoming WebHttp port can reach
internal helpers (Fx, DiagnosticUtility, XmlSerializerOperationBehavior.Reflector).
Disable PackageValidation for this new package (no baseline on NuGet yet).
Part of porting WebHttpBinding to .NET (Core) WCF client (issues #46, #1413).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
System.ServiceModel.sln | 30 +++++++++
.../src/System.ServiceModel.Primitives.csproj | 1 +
.../src/Resources/Strings.resx | 66 +++++++++++++++++++
.../src/System.ServiceModel.Web.csproj | 30 +++++++++
.../System/ServiceModel/WebPackageMarker.cs | 13 ++++
.../System.ServiceModel.Web.Tests.csproj | 15 +++++
6 files changed, 155 insertions(+)
create mode 100644 src/System.ServiceModel.Web/src/Resources/Strings.resx
create mode 100644 src/System.ServiceModel.Web/src/System.ServiceModel.Web.csproj
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/WebPackageMarker.cs
create mode 100644 src/System.ServiceModel.Web/tests/System.ServiceModel.Web.Tests.csproj
diff --git a/System.ServiceModel.sln b/System.ServiceModel.sln
index 16c21a1eee1..e02a9cd2c6d 100644
--- a/System.ServiceModel.sln
+++ b/System.ServiceModel.sln
@@ -89,6 +89,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Binding.UDS.IntegrationTest
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ServiceModel.NetNamedPipe.Tests", "src\System.ServiceModel.NetNamedPipe\tests\System.ServiceModel.NetNamedPipe.Tests.csproj", "{FD188537-21AF-48C5-84FF-F8758F4C42A8}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ServiceModel.Web", "src\System.ServiceModel.Web\src\System.ServiceModel.Web.csproj", "{B9669945-F041-4088-9949-20DC0AE9753D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ServiceModel.Web.Tests", "src\System.ServiceModel.Web\tests\System.ServiceModel.Web.Tests.csproj", "{76E6B222-8CC8-44F4-A1C6-862706CCE788}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ServiceModel.Http.Ref", "src\System.ServiceModel.Http\ref\System.ServiceModel.Http.Ref.csproj", "{34CCEAC9-43B1-4654-BF2F-0B40EC7AD016}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ServiceModel.NetTcp.Ref", "src\System.ServiceModel.NetTcp\ref\System.ServiceModel.NetTcp.Ref.csproj", "{81843CB2-E128-427B-9920-EEDBBBD4CB0B}"
@@ -615,6 +619,30 @@ Global
{FD188537-21AF-48C5-84FF-F8758F4C42A8}.Release|x64.Build.0 = Release|Any CPU
{FD188537-21AF-48C5-84FF-F8758F4C42A8}.Release|x86.ActiveCfg = Release|Any CPU
{FD188537-21AF-48C5-84FF-F8758F4C42A8}.Release|x86.Build.0 = Release|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Debug|x64.Build.0 = Debug|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Debug|x86.Build.0 = Debug|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Release|x64.ActiveCfg = Release|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Release|x64.Build.0 = Release|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Release|x86.ActiveCfg = Release|Any CPU
+ {B9669945-F041-4088-9949-20DC0AE9753D}.Release|x86.Build.0 = Release|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Debug|x64.Build.0 = Debug|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Debug|x86.Build.0 = Debug|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Release|Any CPU.Build.0 = Release|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Release|x64.ActiveCfg = Release|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Release|x64.Build.0 = Release|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Release|x86.ActiveCfg = Release|Any CPU
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788}.Release|x86.Build.0 = Release|Any CPU
{34CCEAC9-43B1-4654-BF2F-0B40EC7AD016}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34CCEAC9-43B1-4654-BF2F-0B40EC7AD016}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34CCEAC9-43B1-4654-BF2F-0B40EC7AD016}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -760,6 +788,8 @@ Global
{88918456-A2B2-431F-BB95-BAAD2818BFC7} = {DFDC71CF-6E65-481D-99D7-C35ED7EF6D4E}
{B7C7D4F1-DE4D-421B-9CE9-C7320A503D58} = {D6302510-AB10-4775-BCE9-98FA96FDEB76}
{FD188537-21AF-48C5-84FF-F8758F4C42A8} = {DFDC71CF-6E65-481D-99D7-C35ED7EF6D4E}
+ {B9669945-F041-4088-9949-20DC0AE9753D} = {D6302510-AB10-4775-BCE9-98FA96FDEB76}
+ {76E6B222-8CC8-44F4-A1C6-862706CCE788} = {DFDC71CF-6E65-481D-99D7-C35ED7EF6D4E}
{0CB62138-1E7D-4D9D-B7BF-4FCB79BC8EAA} = {D6302510-AB10-4775-BCE9-98FA96FDEB76}
{7D7DACFC-D1DA-48C0-9BA1-C3A414780658} = {D6302510-AB10-4775-BCE9-98FA96FDEB76}
EndGlobalSection
diff --git a/src/System.ServiceModel.Primitives/src/System.ServiceModel.Primitives.csproj b/src/System.ServiceModel.Primitives/src/System.ServiceModel.Primitives.csproj
index 72f157b96ab..44d90989bd6 100644
--- a/src/System.ServiceModel.Primitives/src/System.ServiceModel.Primitives.csproj
+++ b/src/System.ServiceModel.Primitives/src/System.ServiceModel.Primitives.csproj
@@ -25,6 +25,7 @@
+
diff --git a/src/System.ServiceModel.Web/src/Resources/Strings.resx b/src/System.ServiceModel.Web/src/Resources/Strings.resx
new file mode 100644
index 00000000000..ed4cd3e41cb
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/Strings.resx
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
diff --git a/src/System.ServiceModel.Web/src/System.ServiceModel.Web.csproj b/src/System.ServiceModel.Web/src/System.ServiceModel.Web.csproj
new file mode 100644
index 00000000000..f5999fffadc
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System.ServiceModel.Web.csproj
@@ -0,0 +1,30 @@
+
+
+ $(WcfAssemblyVersion)
+ Microsoft
+ System.ServiceModel.Web
+ true
+ true
+ $(Ship_WcfPackages)
+ $(DotNetVersion)
+ System.ServiceModel
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+ true
+ FxResources.$(AssemblyName).SR
+ false
+ System.SR
+
+
+
diff --git a/src/System.ServiceModel.Web/src/System/ServiceModel/WebPackageMarker.cs b/src/System.ServiceModel.Web/src/System/ServiceModel/WebPackageMarker.cs
new file mode 100644
index 00000000000..d8edd435304
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/ServiceModel/WebPackageMarker.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// Placeholder so the assembly has at least one type until the WebHttpBinding port lands.
+// Will be removed once real source files (UriTemplate, WebHttpBinding, etc.) are checked in.
+
+namespace System.ServiceModel
+{
+ internal static class WebPackageMarker
+ {
+ }
+}
diff --git a/src/System.ServiceModel.Web/tests/System.ServiceModel.Web.Tests.csproj b/src/System.ServiceModel.Web/tests/System.ServiceModel.Web.Tests.csproj
new file mode 100644
index 00000000000..92510aeb0c5
--- /dev/null
+++ b/src/System.ServiceModel.Web/tests/System.ServiceModel.Web.Tests.csproj
@@ -0,0 +1,15 @@
+
+
+ $(UnitTestTargetFrameworks)
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+
From 66b26dcd6726692d30fd6c94324b7b9122a7ca0c Mon Sep 17 00:00:00 2001
From: Copilot <223556219+Copilot@users.noreply.github.com>
Date: Sat, 13 Jun 2026 20:05:16 +0300
Subject: [PATCH 02/22] Port UriTemplate cluster from .NET Framework reference
source
Port 19 UriTemplate* source files (~4,800 LOC) from the MIT-licensed
Microsoft Reference Source mirror in mono/mono (commit 0f53e9e1) into
the new System.ServiceModel.Web package.
Files ported under src/System.ServiceModel.Web/src/System/:
- UriTemplate.cs (top-level public class, template parsing + binding)
- UriTemplateTable.cs (thread-safe template table, Match/MatchSingle)
- UriTemplateMatch.cs (match result with BoundVariables, QueryParameters)
- UriTemplateMatchException.cs
- UriTemplateEquivalenceComparer.cs
- UriTemplateHelpers.cs (internal escape/comparer helpers)
- UriTemplatePartType.cs (enum)
- UriTemplate{Path,Variable,Compound,Literal}*PathSegment.cs (segment AST)
- UriTemplate{Literal,Variable}QueryValue.cs (query AST)
- UriTemplatePathPartiallyEquivalentSet.cs (internal trie helper)
- UriTemplateTableMatchCandidate.cs (internal struct)
- UriTemplateTrieNode.cs (trie-based matching engine, ~960 LOC)
- UriTemplate{Trie,Path}*.cs (internal trie cursors / enums)
Plus a small companion type:
- System/Collections/ObjectModel/FreezableCollection.cs (internal, ported from
System.ServiceModel/System/Collections/ObjectModel; used by UriTemplateTable)
Transformations applied to every file:
1. MS copyright header swapped for .NET Foundation MIT header.
2. SR.GetString(...) calls rewritten to SR.Format(...) (the modern convention
in dotnet/wcf; string.Format with a single-arg pattern is a no-op).
3. No changes to logic, type signatures, or internal layout - byte-for-byte
equivalent to the reference source after these mechanical edits.
42 new SR strings added to Resources/Strings.resx (ObjectIsReadOnly +
41 UT* error messages); XLF localization files auto-generated by
Microsoft.DotNet.XliffTasks for all 13 target locales.
Build: 0 warnings, 0 errors.
Deps satisfied via InternalsVisibleTo from previous commit:
- DiagnosticUtility.ExceptionUtility.ThrowHelperError/Argument/ArgumentNull
- Fx.Assert
Part of porting WebHttpBinding to .NET (Core) WCF client (issues #46, #1413).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../src/Resources/Strings.resx | 126 ++
.../src/Resources/xlf/Strings.cs.xlf | 217 +++
.../src/Resources/xlf/Strings.de.xlf | 217 +++
.../src/Resources/xlf/Strings.es.xlf | 217 +++
.../src/Resources/xlf/Strings.fr.xlf | 217 +++
.../src/Resources/xlf/Strings.it.xlf | 217 +++
.../src/Resources/xlf/Strings.ja.xlf | 217 +++
.../src/Resources/xlf/Strings.ko.xlf | 217 +++
.../src/Resources/xlf/Strings.pl.xlf | 217 +++
.../src/Resources/xlf/Strings.pt-BR.xlf | 217 +++
.../src/Resources/xlf/Strings.ru.xlf | 217 +++
.../src/Resources/xlf/Strings.tr.xlf | 217 +++
.../src/Resources/xlf/Strings.zh-Hans.xlf | 217 +++
.../src/Resources/xlf/Strings.zh-Hant.xlf | 217 +++
.../ObjectModel/FreezableCollection.cs | 78 +
.../System/ServiceModel/WebPackageMarker.cs | 13 -
.../src/System/UriTemplate.cs | 1731 +++++++++++++++++
.../System/UriTemplateCompoundPathSegment.cs | 486 +++++
.../System/UriTemplateEquivalenceComparer.cs | 56 +
.../src/System/UriTemplateHelpers.cs | 387 ++++
.../System/UriTemplateLiteralPathSegment.cs | 142 ++
.../System/UriTemplateLiteralQueryValue.cs | 84 +
.../src/System/UriTemplateMatch.cs | 176 ++
.../src/System/UriTemplateMatchException.cs | 29 +
.../src/System/UriTemplatePartType.cs | 12 +
.../UriTemplatePathPartiallyEquivalentSet.cs | 40 +
.../src/System/UriTemplatePathSegment.cs | 88 +
.../src/System/UriTemplateQueryValue.cs | 104 +
.../src/System/UriTemplateTable.cs | 607 ++++++
.../System/UriTemplateTableMatchCandidate.cs | 41 +
.../UriTemplateTrieIntraNodeLocation.cs | 14 +
.../src/System/UriTemplateTrieLocation.cs | 17 +
.../src/System/UriTemplateTrieNode.cs | 805 ++++++++
.../System/UriTemplateVariablePathSegment.cs | 68 +
.../System/UriTemplateVariableQueryValue.cs | 48 +
35 files changed, 7960 insertions(+), 13 deletions(-)
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.cs.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.de.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.es.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.fr.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.it.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.ja.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.ko.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.pl.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.pt-BR.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.ru.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.tr.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.zh-Hans.xlf
create mode 100644 src/System.ServiceModel.Web/src/Resources/xlf/Strings.zh-Hant.xlf
create mode 100644 src/System.ServiceModel.Web/src/System/Collections/ObjectModel/FreezableCollection.cs
delete mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/WebPackageMarker.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplate.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateCompoundPathSegment.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateEquivalenceComparer.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateHelpers.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateLiteralPathSegment.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateLiteralQueryValue.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateMatch.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateMatchException.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplatePartType.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplatePathPartiallyEquivalentSet.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplatePathSegment.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateQueryValue.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateTable.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateTableMatchCandidate.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateTrieIntraNodeLocation.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateTrieLocation.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateTrieNode.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateVariablePathSegment.cs
create mode 100644 src/System.ServiceModel.Web/src/System/UriTemplateVariableQueryValue.cs
diff --git a/src/System.ServiceModel.Web/src/Resources/Strings.resx b/src/System.ServiceModel.Web/src/Resources/Strings.resx
index ed4cd3e41cb..f72f8ffc0c4 100644
--- a/src/System.ServiceModel.Web/src/Resources/Strings.resx
+++ b/src/System.ServiceModel.Web/src/Resources/Strings.resx
@@ -63,4 +63,130 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ Object is read-only.
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.cs.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.cs.xlf
new file mode 100644
index 00000000000..8839e3a6eaa
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.cs.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.de.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.de.xlf
new file mode 100644
index 00000000000..9e2009ce2a7
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.de.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.es.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.es.xlf
new file mode 100644
index 00000000000..89e433e4320
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.es.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.fr.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.fr.xlf
new file mode 100644
index 00000000000..27aed855277
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.fr.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.it.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.it.xlf
new file mode 100644
index 00000000000..27651a686de
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.it.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.ja.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.ja.xlf
new file mode 100644
index 00000000000..9ddb59596e3
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.ja.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.ko.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.ko.xlf
new file mode 100644
index 00000000000..3a1411bab6d
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.ko.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.pl.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.pl.xlf
new file mode 100644
index 00000000000..9f82082d33d
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.pl.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.pt-BR.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.pt-BR.xlf
new file mode 100644
index 00000000000..e981caf7e15
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.pt-BR.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.ru.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.ru.xlf
new file mode 100644
index 00000000000..82e6f751b63
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.ru.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.tr.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.tr.xlf
new file mode 100644
index 00000000000..c7d10df9bcd
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.tr.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.zh-Hans.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.zh-Hans.xlf
new file mode 100644
index 00000000000..28fbaa0bb5c
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.zh-Hans.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.zh-Hant.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.zh-Hant.xlf
new file mode 100644
index 00000000000..2e8a272c378
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.zh-Hant.xlf
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Bind parameters cannot be null or empty for path variable '{0}'.
+ Bind parameters cannot be null or empty for path variable '{0}'.
+
+
+
+ Object is read-only.
+ Object is read-only.
+
+
+
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+ The additional default value with key '{0}' is invalid because the UriTemplate '{1}' does not contain a variable with that name.
+
+
+
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+ The base address must be an absolute URI with a scheme of http or https and no query or fragment.
+
+
+
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+ UriTemplate.BindByName was called with an empty key in the parameters collection.
+
+
+
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+ UriTemplate '{0}' has no variables; UriTemplate.BindByPosition cannot be called with {1} value(s).
+
+
+
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+ UriTemplate '{0}' has {1} path variable(s) and {2} query variable(s) but was passed {3} value(s).
+
+
+
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+ The key '{0}' appears as both a literal segment and as a name in the name-value collection passed to UriTemplate.BindByName.
+
+
+
+ Cannot look up compound segment results before a successful Match has been performed.
+ Cannot look up compound segment results before a successful Match has been performed.
+
+
+
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+ UriTemplate '{0}' has a compound path segment '{1}' that contains the variable '{2}' for which a default value was specified. Variables in compound segments cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+ UriTemplate '{0}' has an additional default value for variable '{1}', but that variable appears inside a compound segment and cannot have a default value.
+
+
+
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+ UriTemplate '{0}' has a query value variable '{1}' with default value '{2}'. Query variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+ UriTemplate '{0}' has an additional default value for query variable '{1}'. Query variables cannot have default values.
+
+
+
+ The Defaults dictionary on the UriTemplate is immutable.
+ The Defaults dictionary on the UriTemplate is immutable.
+
+
+
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+ UriTemplate '{0}' contains compound path segment '{1}' with two adjacent variables. Adjacent variables in compound segments are not supported.
+
+
+
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+ UriTemplate '{0}' has variable '{1}' with default value '{2}'. The default value of a path variable cannot be null, empty, or contain a path separator.
+
+
+
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+ The segment or query part '{0}' has an invalid format. Variable names must be enclosed in matching braces.
+
+
+
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+ UriTemplate '{0}' has invalid variable declaration '{1}'.
+
+
+
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+ UriTemplate '{0}' has invalid usage of wildcard '{1}'. Wildcards are only allowed as the last path segment and cannot be combined with other characters in the same segment.
+
+
+
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+ UriTemplate '{0}' has an additional default value for variable '{1}' that is null. Nullable defaults are only allowed when declared inline in the template.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by variable '{2}' which is not nullable. All path variables after a nullable variable must also be nullable.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+ UriTemplate '{0}' has nullable default for variable '{1}' but that variable is followed by literal segment '{2}'. Path variables with nullable defaults cannot be followed by literal segments.
+
+
+
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+ UriTemplate '{0}' has nullable default for variable '{1}'. Path variables with nullable defaults cannot be followed by a wildcard segment.
+
+
+
+ UriTemplate '{0}' has a query string that ends in '&'.
+ UriTemplate '{0}' has a query string that ends in '&'.
+
+
+
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+ UriTemplate '{0}' has a query value that is a compound (mixed literal and variable) expression. Query values must be either pure literals or pure variables.
+
+
+
+ UriTemplate '{0}' has a query parameter with an empty name.
+ UriTemplate '{0}' has a query parameter with an empty name.
+
+
+
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+ UriTemplate '{0}' has a query parameter with a variable name. Query parameter names must be literals.
+
+
+
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+ UriTemplate '{0}' has multiple query parameters with the same name. Query parameter names must be unique within a template.
+
+
+
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has wildcard variable '{1}' with default value '{2}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+ UriTemplate '{0}' has additional default value for wildcard variable '{1}'. Wildcard variables cannot have default values.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings. Conflicting templates: '{0}'.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+ The UriTemplateTable's BaseAddress must be set before this operation can be performed.
+
+
+
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+ The BaseAddress of the UriTemplateTable cannot be changed after the table has been made read-only.
+
+
+
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+ UriTemplateTable contains duplicate templates: '{0}' and '{1}'.
+
+
+
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+ The KeyValuePairs collection of the UriTemplateTable cannot be empty.
+
+
+
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+ '{0}' is not a valid UriTemplate string for use as a key in a UriTemplateTable.
+
+
+
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+ The URI matched multiple UriTemplates in this UriTemplateTable. Use Match() instead of MatchSingle() to get all results.
+
+
+
+ The base address of the UriTemplateTable must be an absolute URI.
+ The base address of the UriTemplateTable must be an absolute URI.
+
+
+
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+ A UriTemplate key in the UriTemplateTable's KeyValuePairs collection cannot be null.
+
+
+
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+ UriTemplateTable contains templates with ambiguous query strings: '{0}'.
+
+
+
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+ UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/System.ServiceModel.Web/src/System/Collections/ObjectModel/FreezableCollection.cs b/src/System.ServiceModel.Web/src/System/Collections/ObjectModel/FreezableCollection.cs
new file mode 100644
index 00000000000..455fdc6720b
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/Collections/ObjectModel/FreezableCollection.cs
@@ -0,0 +1,78 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Collections.ObjectModel
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ServiceModel;
+
+ internal class FreezableCollection : Collection, ICollection
+ {
+ bool frozen;
+
+ public FreezableCollection()
+ : base()
+ {
+ }
+
+ public FreezableCollection(IList list)
+ : base(list)
+ {
+ }
+
+ public bool IsFrozen
+ {
+ get
+ {
+ return this.frozen;
+ }
+ }
+
+ bool ICollection.IsReadOnly
+ {
+ get
+ {
+ return this.frozen;
+ }
+ }
+
+ public void Freeze()
+ {
+ this.frozen = true;
+ }
+
+ protected override void ClearItems()
+ {
+ ThrowIfFrozen();
+ base.ClearItems();
+ }
+
+ protected override void InsertItem(int index, T item)
+ {
+ ThrowIfFrozen();
+ base.InsertItem(index, item);
+ }
+
+ protected override void RemoveItem(int index)
+ {
+ ThrowIfFrozen();
+ base.RemoveItem(index);
+ }
+
+ protected override void SetItem(int index, T item)
+ {
+ ThrowIfFrozen();
+ base.SetItem(index, item);
+ }
+
+ void ThrowIfFrozen()
+ {
+ if (this.frozen)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.ObjectIsReadOnly));
+ }
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/ServiceModel/WebPackageMarker.cs b/src/System.ServiceModel.Web/src/System/ServiceModel/WebPackageMarker.cs
deleted file mode 100644
index d8edd435304..00000000000
--- a/src/System.ServiceModel.Web/src/System/ServiceModel/WebPackageMarker.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-// Placeholder so the assembly has at least one type until the WebHttpBinding port lands.
-// Will be removed once real source files (UriTemplate, WebHttpBinding, etc.) are checked in.
-
-namespace System.ServiceModel
-{
- internal static class WebPackageMarker
- {
- }
-}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplate.cs b/src/System.ServiceModel.Web/src/System/UriTemplate.cs
new file mode 100644
index 00000000000..e8060d89aae
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplate.cs
@@ -0,0 +1,1731 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Concurrent;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Collections.Specialized;
+ using System.Runtime;
+ using System.ServiceModel;
+ using System.ServiceModel.Channels;
+ using System.Text;
+ using System.Threading;
+ using System.Runtime.CompilerServices;
+ using System.Globalization;
+
+ [TypeForwardedFrom("System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
+ public class UriTemplate
+ {
+ internal readonly int firstOptionalSegment;
+
+ internal readonly string originalTemplate;
+ internal readonly Dictionary queries; // keys are original case specified in UriTemplate constructor, dictionary ignores case
+ internal readonly List segments;
+ internal const string WildcardPath = "*";
+ readonly Dictionary additionalDefaults; // keys are original case specified in UriTemplate constructor, dictionary ignores case
+ readonly string fragment;
+
+ readonly bool ignoreTrailingSlash;
+
+ const string NullableDefault = "null";
+ readonly WildcardInfo wildcard;
+ IDictionary defaults;
+ ConcurrentDictionary unescapedDefaults;
+
+ VariablesCollection variables;
+
+ // constructors validates that template is well-formed
+ public UriTemplate(string template)
+ : this(template, false)
+ {
+ }
+ public UriTemplate(string template, bool ignoreTrailingSlash)
+ : this(template, ignoreTrailingSlash, null)
+ {
+ }
+ public UriTemplate(string template, IDictionary additionalDefaults)
+ : this(template, false, additionalDefaults)
+ {
+ }
+ public UriTemplate(string template, bool ignoreTrailingSlash, IDictionary additionalDefaults)
+ {
+ if (template == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("template");
+ }
+ this.originalTemplate = template;
+ this.ignoreTrailingSlash = ignoreTrailingSlash;
+ this.segments = new List();
+ this.queries = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ // parse it
+ string pathTemplate;
+ string queryTemplate;
+ // ignore a leading slash
+ if (template.StartsWith("/", StringComparison.Ordinal))
+ {
+ template = template.Substring(1);
+ }
+ // pull out fragment
+ int fragmentStart = template.IndexOf('#');
+ if (fragmentStart == -1)
+ {
+ this.fragment = "";
+ }
+ else
+ {
+ this.fragment = template.Substring(fragmentStart + 1);
+ template = template.Substring(0, fragmentStart);
+ }
+ // pull out path and query
+ int queryStart = template.IndexOf('?');
+ if (queryStart == -1)
+ {
+ queryTemplate = string.Empty;
+ pathTemplate = template;
+ }
+ else
+ {
+ queryTemplate = template.Substring(queryStart + 1);
+ pathTemplate = template.Substring(0, queryStart);
+ }
+ template = null; // to ensure we don't accidentally reference this variable any more
+
+ // setup path template and validate
+ if (!string.IsNullOrEmpty(pathTemplate))
+ {
+ int startIndex = 0;
+ while (startIndex < pathTemplate.Length)
+ {
+ // Identify the next segment
+ int endIndex = pathTemplate.IndexOf('/', startIndex);
+ string segment;
+ if (endIndex != -1)
+ {
+ segment = pathTemplate.Substring(startIndex, endIndex + 1 - startIndex);
+ startIndex = endIndex + 1;
+ }
+ else
+ {
+ segment = pathTemplate.Substring(startIndex);
+ startIndex = pathTemplate.Length;
+ }
+ // Checking for wildcard segment ("*") or ("{*}")
+ UriTemplatePartType wildcardType;
+ if ((startIndex == pathTemplate.Length) &&
+ UriTemplateHelpers.IsWildcardSegment(segment, out wildcardType))
+ {
+ switch (wildcardType)
+ {
+ case UriTemplatePartType.Literal:
+ this.wildcard = new WildcardInfo(this);
+ break;
+
+ case UriTemplatePartType.Variable:
+ this.wildcard = new WildcardInfo(this, segment);
+ break;
+
+ default:
+ Fx.Assert("Error in identifying the type of the wildcard segment");
+ break;
+ }
+ }
+ else
+ {
+ this.segments.Add(UriTemplatePathSegment.CreateFromUriTemplate(segment, this));
+ }
+ }
+ }
+
+ // setup query template and validate
+ if (!string.IsNullOrEmpty(queryTemplate))
+ {
+ int startIndex = 0;
+ while (startIndex < queryTemplate.Length)
+ {
+ // Identify the next query part
+ int endIndex = queryTemplate.IndexOf('&', startIndex);
+ int queryPartStart = startIndex;
+ int queryPartEnd;
+ if (endIndex != -1)
+ {
+ queryPartEnd = endIndex;
+ startIndex = endIndex + 1;
+ if (startIndex >= queryTemplate.Length)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTQueryCannotEndInAmpersand, this.originalTemplate)));
+ }
+ }
+ else
+ {
+ queryPartEnd = queryTemplate.Length;
+ startIndex = queryTemplate.Length;
+ }
+ // Checking query part type; identifying key and value
+ int equalSignIndex = queryTemplate.IndexOf('=', queryPartStart, queryPartEnd - queryPartStart);
+ string key;
+ string value;
+ if (equalSignIndex >= 0)
+ {
+ key = queryTemplate.Substring(queryPartStart, equalSignIndex - queryPartStart);
+ value = queryTemplate.Substring(equalSignIndex + 1, queryPartEnd - equalSignIndex - 1);
+ }
+ else
+ {
+ key = queryTemplate.Substring(queryPartStart, queryPartEnd - queryPartStart);
+ value = null;
+ }
+ if (string.IsNullOrEmpty(key))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTQueryCannotHaveEmptyName, this.originalTemplate)));
+ }
+ if (UriTemplateHelpers.IdentifyPartType(key) != UriTemplatePartType.Literal)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("template", SR.Format(
+ SR.UTQueryMustHaveLiteralNames, this.originalTemplate));
+ }
+ // Adding a new entry to the queries dictionary
+ key = UrlUtility.UrlDecode(key, Encoding.UTF8);
+ if (this.queries.ContainsKey(key))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTQueryNamesMustBeUnique, this.originalTemplate)));
+ }
+ this.queries.Add(key, UriTemplateQueryValue.CreateFromUriTemplate(value, this));
+ }
+ }
+
+ // Process additional defaults (if has some) :
+ if (additionalDefaults != null)
+ {
+ if (this.variables == null)
+ {
+ if (additionalDefaults.Count > 0)
+ {
+ this.additionalDefaults = new Dictionary(additionalDefaults, StringComparer.OrdinalIgnoreCase);
+ }
+ }
+ else
+ {
+ foreach (KeyValuePair kvp in additionalDefaults)
+ {
+ string uppercaseKey = kvp.Key.ToUpperInvariant();
+ if ((this.variables.DefaultValues != null) && this.variables.DefaultValues.ContainsKey(uppercaseKey))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("additionalDefaults",
+ SR.Format(SR.UTAdditionalDefaultIsInvalid, kvp.Key, this.originalTemplate));
+ }
+ if (this.variables.PathSegmentVariableNames.Contains(uppercaseKey))
+ {
+ this.variables.AddDefaultValue(uppercaseKey, kvp.Value);
+ }
+ else if (this.variables.QueryValueVariableNames.Contains(uppercaseKey))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTDefaultValueToQueryVarFromAdditionalDefaults, this.originalTemplate,
+ uppercaseKey)));
+ }
+ else if (string.Compare(kvp.Value, UriTemplate.NullableDefault, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTNullableDefaultAtAdditionalDefaults, this.originalTemplate,
+ uppercaseKey)));
+ }
+ else
+ {
+ if (this.additionalDefaults == null)
+ {
+ this.additionalDefaults = new Dictionary(additionalDefaults.Count, StringComparer.OrdinalIgnoreCase);
+ }
+ this.additionalDefaults.Add(kvp.Key, kvp.Value);
+ }
+ }
+ }
+ }
+
+ // Validate defaults (if should)
+ if ((this.variables != null) && (this.variables.DefaultValues != null))
+ {
+ this.variables.ValidateDefaults(out this.firstOptionalSegment);
+ }
+ else
+ {
+ this.firstOptionalSegment = this.segments.Count;
+ }
+ }
+
+ public IDictionary Defaults
+ {
+ get
+ {
+ if (this.defaults == null)
+ {
+ Interlocked.CompareExchange>(ref this.defaults, new UriTemplateDefaults(this), null);
+ }
+ return this.defaults;
+ }
+ }
+ public bool IgnoreTrailingSlash
+ {
+ get
+ {
+ return this.ignoreTrailingSlash;
+ }
+ }
+ public ReadOnlyCollection PathSegmentVariableNames
+ {
+ get
+ {
+ if (this.variables == null)
+ {
+ return VariablesCollection.EmptyCollection;
+ }
+ else
+ {
+ return this.variables.PathSegmentVariableNames;
+ }
+ }
+ }
+ public ReadOnlyCollection QueryValueVariableNames
+ {
+ get
+ {
+ if (this.variables == null)
+ {
+ return VariablesCollection.EmptyCollection;
+ }
+ else
+ {
+ return this.variables.QueryValueVariableNames;
+ }
+ }
+ }
+
+ internal bool HasNoVariables
+ {
+ get
+ {
+ return (this.variables == null);
+ }
+ }
+ internal bool HasWildcard
+ {
+ get
+ {
+ return (this.wildcard != null);
+ }
+ }
+
+ // make a Uri by subbing in the values, throw on bad input
+ public Uri BindByName(Uri baseAddress, IDictionary parameters)
+ {
+ return BindByName(baseAddress, parameters, false);
+ }
+ public Uri BindByName(Uri baseAddress, IDictionary parameters, bool omitDefaults)
+ {
+ if (baseAddress == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("baseAddress");
+ }
+ if (!baseAddress.IsAbsoluteUri)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("baseAddress", SR.Format(
+ SR.UTBadBaseAddress));
+ }
+
+ BindInformation bindInfo;
+ if (this.variables == null)
+ {
+ bindInfo = PrepareBindInformation(parameters, omitDefaults);
+ }
+ else
+ {
+ bindInfo = this.variables.PrepareBindInformation(parameters, omitDefaults);
+ }
+ return Bind(baseAddress, bindInfo, omitDefaults);
+ }
+ public Uri BindByName(Uri baseAddress, NameValueCollection parameters)
+ {
+ return BindByName(baseAddress, parameters, false);
+ }
+ public Uri BindByName(Uri baseAddress, NameValueCollection parameters, bool omitDefaults)
+ {
+ if (baseAddress == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("baseAddress");
+ }
+ if (!baseAddress.IsAbsoluteUri)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("baseAddress", SR.Format(
+ SR.UTBadBaseAddress));
+ }
+
+ BindInformation bindInfo;
+ if (this.variables == null)
+ {
+ bindInfo = PrepareBindInformation(parameters, omitDefaults);
+ }
+ else
+ {
+ bindInfo = this.variables.PrepareBindInformation(parameters, omitDefaults);
+ }
+ return Bind(baseAddress, bindInfo, omitDefaults);
+ }
+ public Uri BindByPosition(Uri baseAddress, params string[] values)
+ {
+ if (baseAddress == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("baseAddress");
+ }
+ if (!baseAddress.IsAbsoluteUri)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("baseAddress", SR.Format(
+ SR.UTBadBaseAddress));
+ }
+
+ BindInformation bindInfo;
+ if (this.variables == null)
+ {
+ if (values.Length > 0)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.Format(
+ SR.UTBindByPositionNoVariables, this.originalTemplate, values.Length)));
+ }
+ bindInfo = new BindInformation(this.additionalDefaults);
+ }
+ else
+ {
+ bindInfo = this.variables.PrepareBindInformation(values);
+ }
+ return Bind(baseAddress, bindInfo, false);
+ }
+
+ // A note about UriTemplate equivalency:
+ // The introduction of defaults and, more over, terminal defaults, broke the simple
+ // intuative notion of equivalency between templates. We will define equivalent
+ // templates as such based on the structure of them and not based on the set of uri
+ // that are matched by them. The result is that, even though they do not match the
+ // same set of uri's, the following templates are equivalent:
+ // - "/foo/{bar}"
+ // - "/foo/{bar=xyz}"
+ // A direct result from the support for 'terminal defaults' is that the IsPathEquivalentTo
+ // method, which was used both to determine the equivalence between templates, as
+ // well as verify that all the templates, combined together in the same PathEquivalentSet,
+ // are equivalent in thier path is no longer valid for both purposes. We will break
+ // it to two distinct methods, each will be called in a different case.
+ public bool IsEquivalentTo(UriTemplate other)
+ {
+ if (other == null)
+ {
+ return false;
+ }
+ if (other.segments == null || other.queries == null)
+ {
+ // they never are null, but PreSharp is complaining,
+ // and warning suppression isn't working
+ return false;
+ }
+ if (!IsPathFullyEquivalent(other))
+ {
+ return false;
+ }
+ if (!IsQueryEquivalent(other))
+ {
+ return false;
+ }
+ Fx.Assert(UriTemplateEquivalenceComparer.Instance.GetHashCode(this) == UriTemplateEquivalenceComparer.Instance.GetHashCode(other), "bad GetHashCode impl");
+ return true;
+ }
+
+ public UriTemplateMatch Match(Uri baseAddress, Uri candidate)
+ {
+ if (baseAddress == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("baseAddress");
+ }
+ if (!baseAddress.IsAbsoluteUri)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("baseAddress", SR.Format(
+ SR.UTBadBaseAddress));
+ }
+ if (candidate == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("candidate");
+ }
+
+ // ensure that the candidate is 'under' the base address
+ if (!candidate.IsAbsoluteUri)
+ {
+ return null;
+ }
+ string basePath = UriTemplateHelpers.GetUriPath(baseAddress);
+ string candidatePath = UriTemplateHelpers.GetUriPath(candidate);
+ if (candidatePath.Length < basePath.Length)
+ {
+ return null;
+ }
+ if (!candidatePath.StartsWith(basePath, StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ // Identifying the relative segments \ checking matching to the path :
+ int numSegmentsInBaseAddress = baseAddress.Segments.Length;
+ string[] candidateSegments = candidate.Segments;
+ int numMatchedSegments;
+ Collection relativeCandidateSegments;
+ if (!IsCandidatePathMatch(numSegmentsInBaseAddress, candidateSegments,
+ out numMatchedSegments, out relativeCandidateSegments))
+ {
+ return null;
+ }
+ // Checking matching to the query (if should) :
+ NameValueCollection candidateQuery = null;
+ if (!UriTemplateHelpers.CanMatchQueryTrivially(this))
+ {
+ candidateQuery = UriTemplateHelpers.ParseQueryString(candidate.Query);
+ if (!UriTemplateHelpers.CanMatchQueryInterestingly(this, candidateQuery, false))
+ {
+ return null;
+ }
+ }
+
+ // We matched; lets build the UriTemplateMatch
+ return CreateUriTemplateMatch(baseAddress, candidate, null, numMatchedSegments,
+ relativeCandidateSegments, candidateQuery);
+ }
+
+ public override string ToString()
+ {
+ return this.originalTemplate;
+ }
+
+ internal string AddPathVariable(UriTemplatePartType sourceNature, string varDeclaration)
+ {
+ bool hasDefaultValue;
+ return AddPathVariable(sourceNature, varDeclaration, out hasDefaultValue);
+ }
+ internal string AddPathVariable(UriTemplatePartType sourceNature, string varDeclaration,
+ out bool hasDefaultValue)
+ {
+ if (this.variables == null)
+ {
+ this.variables = new VariablesCollection(this);
+ }
+ return this.variables.AddPathVariable(sourceNature, varDeclaration, out hasDefaultValue);
+ }
+ internal string AddQueryVariable(string varDeclaration)
+ {
+ if (this.variables == null)
+ {
+ this.variables = new VariablesCollection(this);
+ }
+ return this.variables.AddQueryVariable(varDeclaration);
+ }
+
+ internal UriTemplateMatch CreateUriTemplateMatch(Uri baseUri, Uri uri, object data,
+ int numMatchedSegments, Collection relativePathSegments, NameValueCollection uriQuery)
+ {
+ UriTemplateMatch result = new UriTemplateMatch();
+ result.RequestUri = uri;
+ result.BaseUri = baseUri;
+ if (uriQuery != null)
+ {
+ result.SetQueryParameters(uriQuery);
+ }
+ result.SetRelativePathSegments(relativePathSegments);
+ result.Data = data;
+ result.Template = this;
+ for (int i = 0; i < numMatchedSegments; i++)
+ {
+ this.segments[i].Lookup(result.RelativePathSegments[i], result.BoundVariables);
+ }
+ if (this.wildcard != null)
+ {
+ this.wildcard.Lookup(numMatchedSegments, result.RelativePathSegments,
+ result.BoundVariables);
+ }
+ else if (numMatchedSegments < this.segments.Count)
+ {
+ BindTerminalDefaults(numMatchedSegments, result.BoundVariables);
+ }
+ if (this.queries.Count > 0)
+ {
+ foreach (KeyValuePair kvp in this.queries)
+ {
+ kvp.Value.Lookup(result.QueryParameters[kvp.Key], result.BoundVariables);
+ //UriTemplateHelpers.AssertCanonical(varName);
+ }
+ }
+ if (this.additionalDefaults != null)
+ {
+ foreach (KeyValuePair kvp in this.additionalDefaults)
+ {
+ result.BoundVariables.Add(kvp.Key, UnescapeDefaultValue(kvp.Value));
+ }
+ }
+ Fx.Assert(result.RelativePathSegments.Count - numMatchedSegments >= 0, "bad segment computation");
+ result.SetWildcardPathSegmentsStart(numMatchedSegments);
+
+ return result;
+ }
+
+ internal bool IsPathPartiallyEquivalentAt(UriTemplate other, int segmentsCount)
+ {
+ // Refer to the note on template equivalency at IsEquivalentTo
+ // This method checks if any uri with given number of segments, which can be matched
+ // by this template, can be also matched by the other template.
+ Fx.Assert(segmentsCount >= this.firstOptionalSegment - 1, "How can that be? The Trie is constructed that way!");
+ Fx.Assert(segmentsCount <= this.segments.Count, "How can that be? The Trie is constructed that way!");
+ Fx.Assert(segmentsCount >= other.firstOptionalSegment - 1, "How can that be? The Trie is constructed that way!");
+ Fx.Assert(segmentsCount <= other.segments.Count, "How can that be? The Trie is constructed that way!");
+ for (int i = 0; i < segmentsCount; ++i)
+ {
+ if (!this.segments[i].IsEquivalentTo(other.segments[i],
+ ((i == segmentsCount - 1) && (this.ignoreTrailingSlash || other.ignoreTrailingSlash))))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ internal bool IsQueryEquivalent(UriTemplate other)
+ {
+ if (this.queries.Count != other.queries.Count)
+ {
+ return false;
+ }
+ foreach (string key in this.queries.Keys)
+ {
+ UriTemplateQueryValue utqv = this.queries[key];
+ UriTemplateQueryValue otherUtqv;
+ if (!other.queries.TryGetValue(key, out otherUtqv))
+ {
+ return false;
+ }
+ if (!utqv.IsEquivalentTo(otherUtqv))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ internal static Uri RewriteUri(Uri uri, string host)
+ {
+ if (!string.IsNullOrEmpty(host))
+ {
+ string originalHostHeader = uri.Host + ((!uri.IsDefaultPort) ? ":" + uri.Port.ToString(CultureInfo.InvariantCulture) : string.Empty);
+ if (!String.Equals(originalHostHeader, host, StringComparison.OrdinalIgnoreCase))
+ {
+ Uri sourceUri = new Uri(String.Format(CultureInfo.InvariantCulture, "{0}://{1}", uri.Scheme, host));
+ return (new UriBuilder(uri) { Host = sourceUri.Host, Port = sourceUri.Port }).Uri;
+ }
+ }
+ return uri;
+ }
+
+ Uri Bind(Uri baseAddress, BindInformation bindInfo, bool omitDefaults)
+ {
+ UriBuilder result = new UriBuilder(baseAddress);
+ int parameterIndex = 0;
+ int lastPathParameter = ((this.variables == null) ? -1 : this.variables.PathSegmentVariableNames.Count - 1);
+ int lastPathParameterToBind;
+ if (lastPathParameter == -1)
+ {
+ lastPathParameterToBind = -1;
+ }
+ else if (omitDefaults)
+ {
+ lastPathParameterToBind = bindInfo.LastNonDefaultPathParameter;
+ }
+ else
+ {
+ lastPathParameterToBind = bindInfo.LastNonNullablePathParameter;
+ }
+ string[] parameters = bindInfo.NormalizedParameters;
+ IDictionary extraQueryParameters = bindInfo.AdditionalParameters;
+ // Binding the path :
+ StringBuilder pathString = new StringBuilder(result.Path);
+ if (pathString[pathString.Length - 1] != '/')
+ {
+ pathString.Append('/');
+ }
+ if (lastPathParameterToBind < lastPathParameter)
+ {
+ // Binding all the parameters we need
+ int segmentIndex = 0;
+ while (parameterIndex <= lastPathParameterToBind)
+ {
+ Fx.Assert(segmentIndex < this.segments.Count,
+ "Calculation of LastNonDefaultPathParameter,lastPathParameter or parameterIndex failed");
+ this.segments[segmentIndex++].Bind(parameters, ref parameterIndex, pathString);
+ }
+ Fx.Assert(parameterIndex == lastPathParameterToBind + 1,
+ "That is the exit criteria from the loop");
+ // Maybe we have some literals yet to bind
+ Fx.Assert(segmentIndex < this.segments.Count,
+ "Calculation of LastNonDefaultPathParameter,lastPathParameter or parameterIndex failed");
+ while (this.segments[segmentIndex].Nature == UriTemplatePartType.Literal)
+ {
+ this.segments[segmentIndex++].Bind(parameters, ref parameterIndex, pathString);
+ Fx.Assert(parameterIndex == lastPathParameterToBind + 1,
+ "We have moved the parameter index in a literal binding");
+ Fx.Assert(segmentIndex < this.segments.Count,
+ "Calculation of LastNonDefaultPathParameter,lastPathParameter or parameterIndex failed");
+ }
+ // We're done; skip to the beggining of the query parameters
+ parameterIndex = lastPathParameter + 1;
+ }
+ else if (this.segments.Count > 0 || this.wildcard != null)
+ {
+ for (int i = 0; i < this.segments.Count; i++)
+ {
+ this.segments[i].Bind(parameters, ref parameterIndex, pathString);
+ }
+ if (this.wildcard != null)
+ {
+ this.wildcard.Bind(parameters, ref parameterIndex, pathString);
+ }
+ }
+ if (this.ignoreTrailingSlash && (pathString[pathString.Length - 1] == '/'))
+ {
+ pathString.Remove(pathString.Length - 1, 1);
+ }
+ result.Path = pathString.ToString();
+ // Binding the query :
+ if ((this.queries.Count != 0) || (extraQueryParameters != null))
+ {
+ StringBuilder query = new StringBuilder("");
+ foreach (string key in this.queries.Keys)
+ {
+ this.queries[key].Bind(key, parameters, ref parameterIndex, query);
+ }
+ if (extraQueryParameters != null)
+ {
+ foreach (string key in extraQueryParameters.Keys)
+ {
+ if (this.queries.ContainsKey(key.ToUpperInvariant()))
+ {
+ // This can only be if the key passed has the same name as some literal key
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("parameters", SR.Format(
+ SR.UTBothLiteralAndNameValueCollectionKey, key));
+ }
+ string value = extraQueryParameters[key];
+ string escapedValue = (string.IsNullOrEmpty(value) ? string.Empty : UrlUtility.UrlEncode(value, Encoding.UTF8));
+ query.AppendFormat("&{0}={1}", UrlUtility.UrlEncode(key, Encoding.UTF8), escapedValue);
+ }
+ }
+ if (query.Length != 0)
+ {
+ query.Remove(0, 1); // remove extra leading '&'
+ }
+ result.Query = query.ToString();
+ }
+ // Adding the fragment (if needed)
+ if (this.fragment != null)
+ {
+ result.Fragment = this.fragment;
+ }
+
+ return result.Uri;
+ }
+ void BindTerminalDefaults(int numMatchedSegments, NameValueCollection boundParameters)
+ {
+ Fx.Assert(!this.HasWildcard, "There are no terminal default when ends with wildcard");
+ Fx.Assert(numMatchedSegments < this.segments.Count, "Otherwise - no defaults to bind");
+ Fx.Assert(this.variables != null, "Otherwise - no default values to bind");
+ Fx.Assert(this.variables.DefaultValues != null, "Otherwise - no default values to bind");
+ for (int i = numMatchedSegments; i < this.segments.Count; i++)
+ {
+ switch (this.segments[i].Nature)
+ {
+ case UriTemplatePartType.Variable:
+ {
+ UriTemplateVariablePathSegment vps = this.segments[i] as UriTemplateVariablePathSegment;
+ Fx.Assert(vps != null, "How can that be? That its nature");
+ this.variables.LookupDefault(vps.VarName, boundParameters);
+ }
+ break;
+
+ default:
+ Fx.Assert("We only support terminal defaults on Variable segments");
+ break;
+ }
+ }
+ }
+
+ bool IsCandidatePathMatch(int numSegmentsInBaseAddress, string[] candidateSegments,
+ out int numMatchedSegments, out Collection relativeSegments)
+ {
+ int numRelativeSegments = candidateSegments.Length - numSegmentsInBaseAddress;
+ Fx.Assert(numRelativeSegments >= 0, "bad segments num");
+ relativeSegments = new Collection();
+ bool isStillMatch = true;
+ int relativeSegmentsIndex = 0;
+ while (isStillMatch && (relativeSegmentsIndex < numRelativeSegments))
+ {
+ string segment = candidateSegments[relativeSegmentsIndex + numSegmentsInBaseAddress];
+ // Mathcing to next regular segment in the template (if there is one); building the wire segment representation
+ if (relativeSegmentsIndex < this.segments.Count)
+ {
+ bool ignoreSlash = (this.ignoreTrailingSlash && (relativeSegmentsIndex == numRelativeSegments - 1));
+ UriTemplateLiteralPathSegment lps = UriTemplateLiteralPathSegment.CreateFromWireData(segment);
+ if (!this.segments[relativeSegmentsIndex].IsMatch(lps, ignoreSlash))
+ {
+ isStillMatch = false;
+ break;
+ }
+ string relPathSeg = Uri.UnescapeDataString(segment);
+ if (lps.EndsWithSlash)
+ {
+ Fx.Assert(relPathSeg.EndsWith("/", StringComparison.Ordinal), "problem with relative path segment");
+ relPathSeg = relPathSeg.Substring(0, relPathSeg.Length - 1); // trim slash
+ }
+ relativeSegments.Add(relPathSeg);
+ }
+ // Checking if the template has a wild card ('*') or a final star var segment ("{*}"
+ else if (this.HasWildcard)
+ {
+ break;
+ }
+ else
+ {
+ isStillMatch = false;
+ break;
+ }
+ relativeSegmentsIndex++;
+ }
+ if (isStillMatch)
+ {
+ numMatchedSegments = relativeSegmentsIndex;
+ // building the wire representation to segments that were matched to a wild card
+ if (relativeSegmentsIndex < numRelativeSegments)
+ {
+ while (relativeSegmentsIndex < numRelativeSegments)
+ {
+ string relPathSeg = Uri.UnescapeDataString(candidateSegments[relativeSegmentsIndex + numSegmentsInBaseAddress]);
+ if (relPathSeg.EndsWith("/", StringComparison.Ordinal))
+ {
+ relPathSeg = relPathSeg.Substring(0, relPathSeg.Length - 1); // trim slash
+ }
+ relativeSegments.Add(relPathSeg);
+ relativeSegmentsIndex++;
+ }
+ }
+ // Checking if we matched all required segments already
+ else if (numMatchedSegments < this.firstOptionalSegment)
+ {
+ isStillMatch = false;
+ }
+ }
+ else
+ {
+ numMatchedSegments = 0;
+ }
+
+ return isStillMatch;
+ }
+
+ bool IsPathFullyEquivalent(UriTemplate other)
+ {
+ // Refer to the note on template equivalency at IsEquivalentTo
+ // This method checks if both templates has a fully equivalent path.
+ if (this.HasWildcard != other.HasWildcard)
+ {
+ return false;
+ }
+ if (this.segments.Count != other.segments.Count)
+ {
+ return false;
+ }
+ for (int i = 0; i < this.segments.Count; ++i)
+ {
+ if (!this.segments[i].IsEquivalentTo(other.segments[i],
+ (i == this.segments.Count - 1) && !this.HasWildcard && (this.ignoreTrailingSlash || other.ignoreTrailingSlash)))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ BindInformation PrepareBindInformation(IDictionary parameters, bool omitDefaults)
+ {
+ if (parameters == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters");
+ }
+
+ IDictionary extraParameters = new Dictionary(UriTemplateHelpers.GetQueryKeyComparer());
+ foreach (KeyValuePair kvp in parameters)
+ {
+ if (string.IsNullOrEmpty(kvp.Key))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("parameters",
+ SR.Format(SR.UTBindByNameCalledWithEmptyKey));
+ }
+
+ extraParameters.Add(kvp);
+ }
+ BindInformation bindInfo;
+ ProcessDefaultsAndCreateBindInfo(omitDefaults, extraParameters, out bindInfo);
+ return bindInfo;
+ }
+ BindInformation PrepareBindInformation(NameValueCollection parameters, bool omitDefaults)
+ {
+ if (parameters == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters");
+ }
+
+ IDictionary extraParameters = new Dictionary(UriTemplateHelpers.GetQueryKeyComparer());
+ foreach (string key in parameters.AllKeys)
+ {
+ if (string.IsNullOrEmpty(key))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("parameters",
+ SR.Format(SR.UTBindByNameCalledWithEmptyKey));
+ }
+
+ extraParameters.Add(key, parameters[key]);
+ }
+ BindInformation bindInfo;
+ ProcessDefaultsAndCreateBindInfo(omitDefaults, extraParameters, out bindInfo);
+ return bindInfo;
+ }
+ void ProcessDefaultsAndCreateBindInfo(bool omitDefaults, IDictionary extraParameters,
+ out BindInformation bindInfo)
+ {
+ Fx.Assert(extraParameters != null, "We are expected to create it at the calling PrepareBindInformation");
+ if (this.additionalDefaults != null)
+ {
+ if (omitDefaults)
+ {
+ foreach (KeyValuePair kvp in this.additionalDefaults)
+ {
+ string extraParameter;
+ if (extraParameters.TryGetValue(kvp.Key, out extraParameter))
+ {
+ if (string.Compare(extraParameter, kvp.Value, StringComparison.Ordinal) == 0)
+ {
+ extraParameters.Remove(kvp.Key);
+ }
+ }
+ }
+ }
+ else
+ {
+ foreach (KeyValuePair kvp in this.additionalDefaults)
+ {
+ if (!extraParameters.ContainsKey(kvp.Key))
+ {
+ extraParameters.Add(kvp.Key, kvp.Value);
+ }
+ }
+ }
+ }
+ if (extraParameters.Count == 0)
+ {
+ extraParameters = null;
+ }
+ bindInfo = new BindInformation(extraParameters);
+ }
+
+ string UnescapeDefaultValue(string escapedValue)
+ {
+ if (string.IsNullOrEmpty(escapedValue))
+ {
+ return escapedValue;
+ }
+ if (this.unescapedDefaults == null)
+ {
+ this.unescapedDefaults = new ConcurrentDictionary(StringComparer.Ordinal);
+ }
+
+ return this.unescapedDefaults.GetOrAdd(escapedValue, Uri.UnescapeDataString);
+ }
+
+ struct BindInformation
+ {
+ IDictionary additionalParameters;
+ int lastNonDefaultPathParameter;
+ int lastNonNullablePathParameter;
+ string[] normalizedParameters;
+
+ public BindInformation(string[] normalizedParameters, int lastNonDefaultPathParameter,
+ int lastNonNullablePathParameter, IDictionary additionalParameters)
+ {
+ this.normalizedParameters = normalizedParameters;
+ this.lastNonDefaultPathParameter = lastNonDefaultPathParameter;
+ this.lastNonNullablePathParameter = lastNonNullablePathParameter;
+ this.additionalParameters = additionalParameters;
+ }
+ public BindInformation(IDictionary additionalParameters)
+ {
+ this.normalizedParameters = null;
+ this.lastNonDefaultPathParameter = -1;
+ this.lastNonNullablePathParameter = -1;
+ this.additionalParameters = additionalParameters;
+ }
+
+ public IDictionary AdditionalParameters
+ {
+ get
+ {
+ return this.additionalParameters;
+ }
+ }
+ public int LastNonDefaultPathParameter
+ {
+ get
+ {
+ return this.lastNonDefaultPathParameter;
+ }
+ }
+ public int LastNonNullablePathParameter
+ {
+ get
+ {
+ return this.lastNonNullablePathParameter;
+ }
+ }
+ public string[] NormalizedParameters
+ {
+ get
+ {
+ return this.normalizedParameters;
+ }
+ }
+ }
+
+ class UriTemplateDefaults : IDictionary
+ {
+ Dictionary defaults;
+ ReadOnlyCollection keys;
+ ReadOnlyCollection values;
+
+ public UriTemplateDefaults(UriTemplate template)
+ {
+ this.defaults = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ if ((template.variables != null) && (template.variables.DefaultValues != null))
+ {
+ foreach (KeyValuePair kvp in template.variables.DefaultValues)
+ {
+ this.defaults.Add(kvp.Key, kvp.Value);
+ }
+ }
+ if (template.additionalDefaults != null)
+ {
+ foreach (KeyValuePair kvp in template.additionalDefaults)
+ {
+ this.defaults.Add(kvp.Key.ToUpperInvariant(), kvp.Value);
+ }
+ }
+ this.keys = new ReadOnlyCollection(new List(this.defaults.Keys));
+ this.values = new ReadOnlyCollection(new List(this.defaults.Values));
+ }
+
+ // ICollection> Members
+ public int Count
+ {
+ get
+ {
+ return this.defaults.Count;
+ }
+ }
+ public bool IsReadOnly
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ // IDictionary Members
+ public ICollection Keys
+ {
+ get
+ {
+ return this.keys;
+ }
+ }
+ public ICollection Values
+ {
+ get
+ {
+ return this.values;
+ }
+ }
+ public string this[string key]
+ {
+ get
+ {
+ return this.defaults[key];
+ }
+ set
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
+ SR.Format(SR.UTDefaultValuesAreImmutable)));
+ }
+ }
+
+ public void Add(string key, string value)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
+ SR.Format(SR.UTDefaultValuesAreImmutable)));
+ }
+
+ public void Add(KeyValuePair item)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
+ SR.Format(SR.UTDefaultValuesAreImmutable)));
+ }
+ public void Clear()
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
+ SR.Format(SR.UTDefaultValuesAreImmutable)));
+ }
+ public bool Contains(KeyValuePair item)
+ {
+ return (this.defaults as ICollection>).Contains(item);
+ }
+ public bool ContainsKey(string key)
+ {
+ return this.defaults.ContainsKey(key);
+ }
+ public void CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ (this.defaults as ICollection>).CopyTo(array, arrayIndex);
+ }
+
+ // IEnumerable> Members
+ public IEnumerator> GetEnumerator()
+ {
+ return this.defaults.GetEnumerator();
+ }
+ public bool Remove(string key)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
+ SR.Format(SR.UTDefaultValuesAreImmutable)));
+ }
+ public bool Remove(KeyValuePair item)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
+ SR.Format(SR.UTDefaultValuesAreImmutable)));
+ }
+
+ // IEnumerable Members
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return this.defaults.GetEnumerator();
+ }
+ public bool TryGetValue(string key, out string value)
+ {
+ return this.defaults.TryGetValue(key, out value);
+ }
+ }
+
+ class VariablesCollection
+ {
+ readonly UriTemplate owner;
+ static ReadOnlyCollection emptyStringCollection = null;
+ Dictionary defaultValues; // key is the variable name (in uppercase; as appear in the variable names lists)
+ int firstNullablePathVariable;
+ List pathSegmentVariableNames; // ToUpperInvariant, in order they occur in the original template string
+ ReadOnlyCollection pathSegmentVariableNamesSnapshot = null;
+ List pathSegmentVariableNature;
+ List queryValueVariableNames; // ToUpperInvariant, in order they occur in the original template string
+ ReadOnlyCollection queryValueVariableNamesSnapshot = null;
+
+ public VariablesCollection(UriTemplate owner)
+ {
+ this.owner = owner;
+ this.pathSegmentVariableNames = new List();
+ this.pathSegmentVariableNature = new List();
+ this.queryValueVariableNames = new List();
+ this.firstNullablePathVariable = -1;
+ }
+
+ public static ReadOnlyCollection EmptyCollection
+ {
+ get
+ {
+ if (emptyStringCollection == null)
+ {
+ emptyStringCollection = new ReadOnlyCollection(new List());
+ }
+ return emptyStringCollection;
+ }
+ }
+
+ public Dictionary DefaultValues
+ {
+ get
+ {
+ return this.defaultValues;
+ }
+ }
+ public ReadOnlyCollection PathSegmentVariableNames
+ {
+ get
+ {
+ if (this.pathSegmentVariableNamesSnapshot == null)
+ {
+ Interlocked.CompareExchange>(ref this.pathSegmentVariableNamesSnapshot, new ReadOnlyCollection(
+ this.pathSegmentVariableNames), null);
+ }
+ return this.pathSegmentVariableNamesSnapshot;
+ }
+ }
+ public ReadOnlyCollection QueryValueVariableNames
+ {
+ get
+ {
+ if (this.queryValueVariableNamesSnapshot == null)
+ {
+ Interlocked.CompareExchange>(ref this.queryValueVariableNamesSnapshot, new ReadOnlyCollection(
+ this.queryValueVariableNames), null);
+ }
+ return this.queryValueVariableNamesSnapshot;
+ }
+ }
+
+ public void AddDefaultValue(string varName, string value)
+ {
+ int varIndex = this.pathSegmentVariableNames.IndexOf(varName);
+ Fx.Assert(varIndex != -1, "Adding default value is restricted to path variables");
+ if ((this.owner.wildcard != null) && this.owner.wildcard.HasVariable &&
+ (varIndex == this.pathSegmentVariableNames.Count - 1))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTStarVariableWithDefaultsFromAdditionalDefaults,
+ this.owner.originalTemplate, varName)));
+ }
+ if (this.pathSegmentVariableNature[varIndex] != UriTemplatePartType.Variable)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTDefaultValueToCompoundSegmentVarFromAdditionalDefaults,
+ this.owner.originalTemplate, varName)));
+ }
+ if (string.IsNullOrEmpty(value) ||
+ (string.Compare(value, UriTemplate.NullableDefault, StringComparison.OrdinalIgnoreCase) == 0))
+ {
+ value = null;
+ }
+ if (this.defaultValues == null)
+ {
+ this.defaultValues = new Dictionary();
+ }
+ this.defaultValues.Add(varName, value);
+ }
+
+ public string AddPathVariable(UriTemplatePartType sourceNature, string varDeclaration, out bool hasDefaultValue)
+ {
+ Fx.Assert(sourceNature != UriTemplatePartType.Literal, "Literal path segments can't be the source for path variables");
+ string varName;
+ string defaultValue;
+ ParseVariableDeclaration(varDeclaration, out varName, out defaultValue);
+ hasDefaultValue = (defaultValue != null);
+ if (varName.IndexOf(UriTemplate.WildcardPath, StringComparison.Ordinal) != -1)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidWildcardInVariableOrLiteral, this.owner.originalTemplate, UriTemplate.WildcardPath)));
+ }
+ string uppercaseVarName = varName.ToUpperInvariant();
+ if (this.pathSegmentVariableNames.Contains(uppercaseVarName) ||
+ this.queryValueVariableNames.Contains(uppercaseVarName))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTVarNamesMustBeUnique, this.owner.originalTemplate, varName)));
+ }
+ this.pathSegmentVariableNames.Add(uppercaseVarName);
+ this.pathSegmentVariableNature.Add(sourceNature);
+ if (hasDefaultValue)
+ {
+ if (defaultValue == string.Empty)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTInvalidDefaultPathValue, this.owner.originalTemplate,
+ varDeclaration, varName)));
+ }
+ if (string.Compare(defaultValue, UriTemplate.NullableDefault, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ defaultValue = null;
+ }
+ if (this.defaultValues == null)
+ {
+ this.defaultValues = new Dictionary();
+ }
+ this.defaultValues.Add(uppercaseVarName, defaultValue);
+ }
+ return uppercaseVarName;
+ }
+ public string AddQueryVariable(string varDeclaration)
+ {
+ string varName;
+ string defaultValue;
+ ParseVariableDeclaration(varDeclaration, out varName, out defaultValue);
+ if (varName.IndexOf(UriTemplate.WildcardPath, StringComparison.Ordinal) != -1)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidWildcardInVariableOrLiteral, this.owner.originalTemplate, UriTemplate.WildcardPath)));
+ }
+ if (defaultValue != null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTDefaultValueToQueryVar, this.owner.originalTemplate,
+ varDeclaration, varName)));
+ }
+ string uppercaseVarName = varName.ToUpperInvariant();
+ if (this.pathSegmentVariableNames.Contains(uppercaseVarName) ||
+ this.queryValueVariableNames.Contains(uppercaseVarName))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTVarNamesMustBeUnique, this.owner.originalTemplate, varName)));
+ }
+ this.queryValueVariableNames.Add(uppercaseVarName);
+ return uppercaseVarName;
+ }
+
+ public void LookupDefault(string varName, NameValueCollection boundParameters)
+ {
+ Fx.Assert(this.defaultValues.ContainsKey(varName), "Otherwise, we don't have a value to bind");
+ boundParameters.Add(varName, owner.UnescapeDefaultValue(this.defaultValues[varName]));
+ }
+
+ public BindInformation PrepareBindInformation(IDictionary parameters, bool omitDefaults)
+ {
+ if (parameters == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters");
+ }
+
+ string[] normalizedParameters = PrepareNormalizedParameters();
+ IDictionary extraParameters = null;
+ foreach (string key in parameters.Keys)
+ {
+ ProcessBindParameter(key, parameters[key], normalizedParameters, ref extraParameters);
+ }
+ BindInformation bindInfo;
+ ProcessDefaultsAndCreateBindInfo(omitDefaults, normalizedParameters, extraParameters, out bindInfo);
+ return bindInfo;
+ }
+ public BindInformation PrepareBindInformation(NameValueCollection parameters, bool omitDefaults)
+ {
+ if (parameters == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters");
+ }
+
+ string[] normalizedParameters = PrepareNormalizedParameters();
+ IDictionary extraParameters = null;
+ foreach (string key in parameters.AllKeys)
+ {
+ ProcessBindParameter(key, parameters[key], normalizedParameters, ref extraParameters);
+ }
+ BindInformation bindInfo;
+ ProcessDefaultsAndCreateBindInfo(omitDefaults, normalizedParameters, extraParameters, out bindInfo);
+ return bindInfo;
+ }
+ public BindInformation PrepareBindInformation(params string[] parameters)
+ {
+ if (parameters == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("values");
+ }
+ if ((parameters.Length < this.pathSegmentVariableNames.Count) ||
+ (parameters.Length > this.pathSegmentVariableNames.Count + this.queryValueVariableNames.Count))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTBindByPositionWrongCount, this.owner.originalTemplate,
+ this.pathSegmentVariableNames.Count, this.queryValueVariableNames.Count,
+ parameters.Length)));
+ }
+
+ string[] normalizedParameters;
+ if (parameters.Length == this.pathSegmentVariableNames.Count + this.queryValueVariableNames.Count)
+ {
+ normalizedParameters = parameters;
+ }
+ else
+ {
+ normalizedParameters = new string[this.pathSegmentVariableNames.Count + this.queryValueVariableNames.Count];
+ parameters.CopyTo(normalizedParameters, 0);
+ for (int i = parameters.Length; i < normalizedParameters.Length; i++)
+ {
+ normalizedParameters[i] = null;
+ }
+ }
+ int lastNonDefaultPathParameter;
+ int lastNonNullablePathParameter;
+ LoadDefaultsAndValidate(normalizedParameters, out lastNonDefaultPathParameter,
+ out lastNonNullablePathParameter);
+ return new BindInformation(normalizedParameters, lastNonDefaultPathParameter,
+ lastNonNullablePathParameter, this.owner.additionalDefaults);
+ }
+ public void ValidateDefaults(out int firstOptionalSegment)
+ {
+ Fx.Assert(this.defaultValues != null, "We are checking this condition from the c'tor");
+ Fx.Assert(this.pathSegmentVariableNames.Count > 0, "Otherwise, how can we have default values");
+ // Finding the first valid nullable defaults
+ for (int i = this.pathSegmentVariableNames.Count - 1; (i >= 0) && (this.firstNullablePathVariable == -1); i--)
+ {
+ string varName = this.pathSegmentVariableNames[i];
+ string defaultValue;
+ if (!this.defaultValues.TryGetValue(varName, out defaultValue))
+ {
+ this.firstNullablePathVariable = i + 1;
+ }
+ else if (defaultValue != null)
+ {
+ this.firstNullablePathVariable = i + 1;
+ }
+ }
+ if (this.firstNullablePathVariable == -1)
+ {
+ this.firstNullablePathVariable = 0;
+ }
+ // Making sure that there are no nullables to the left of the first valid nullable
+ if (this.firstNullablePathVariable > 1)
+ {
+ for (int i = this.firstNullablePathVariable - 2; i >= 0; i--)
+ {
+ string varName = this.pathSegmentVariableNames[i];
+ string defaultValue;
+ if (this.defaultValues.TryGetValue(varName, out defaultValue))
+ {
+ if (defaultValue == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTNullableDefaultMustBeFollowedWithNullables, this.owner.originalTemplate,
+ varName, this.pathSegmentVariableNames[i + 1])));
+ }
+ }
+ }
+ }
+ // Making sure that there are no Literals\WildCards to the right
+ // Based on the fact that only Variable Path Segments support default values,
+ // if firstNullablePathVariable=N and pathSegmentVariableNames.Count=M then
+ // the nature of the last M-N path segments should be StringNature.Variable; otherwise,
+ // there was a literal segment in between. Also, there shouldn't be a wildcard.
+ if (this.firstNullablePathVariable < this.pathSegmentVariableNames.Count)
+ {
+ if (this.owner.HasWildcard)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTNullableDefaultMustNotBeFollowedWithWildcard,
+ this.owner.originalTemplate, this.pathSegmentVariableNames[this.firstNullablePathVariable])));
+ }
+ for (int i = this.pathSegmentVariableNames.Count - 1; i >= this.firstNullablePathVariable; i--)
+ {
+ int segmentIndex = this.owner.segments.Count - (this.pathSegmentVariableNames.Count - i);
+ if (this.owner.segments[segmentIndex].Nature != UriTemplatePartType.Variable)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTNullableDefaultMustNotBeFollowedWithLiteral,
+ this.owner.originalTemplate, this.pathSegmentVariableNames[this.firstNullablePathVariable],
+ this.owner.segments[segmentIndex].OriginalSegment)));
+ }
+ }
+ }
+ // Now that we have the firstNullablePathVariable set, lets calculate the firstOptionalSegment.
+ // We already knows that the last M-N path segments (when M=pathSegmentVariableNames.Count and
+ // N=firstNullablePathVariable) are optional (see the previos comment). We will start there and
+ // move to the left, stopping at the first segment, which is not a variable or is a variable
+ // and doesn't have a default value.
+ int numNullablePathVariables = (this.pathSegmentVariableNames.Count - this.firstNullablePathVariable);
+ firstOptionalSegment = this.owner.segments.Count - numNullablePathVariables;
+ if (!this.owner.HasWildcard)
+ {
+ while (firstOptionalSegment > 0)
+ {
+ UriTemplatePathSegment ps = this.owner.segments[firstOptionalSegment - 1];
+ if (ps.Nature != UriTemplatePartType.Variable)
+ {
+ break;
+ }
+ UriTemplateVariablePathSegment vps = (ps as UriTemplateVariablePathSegment);
+ Fx.Assert(vps != null, "Should be; that's his nature");
+ if (!this.defaultValues.ContainsKey(vps.VarName))
+ {
+ break;
+ }
+ firstOptionalSegment--;
+ }
+ }
+ }
+
+ void AddAdditionalDefaults(ref IDictionary extraParameters)
+ {
+ if (extraParameters == null)
+ {
+ extraParameters = this.owner.additionalDefaults;
+ }
+ else
+ {
+ foreach (KeyValuePair kvp in this.owner.additionalDefaults)
+ {
+ if (!extraParameters.ContainsKey(kvp.Key))
+ {
+ extraParameters.Add(kvp.Key, kvp.Value);
+ }
+ }
+ }
+ }
+ void LoadDefaultsAndValidate(string[] normalizedParameters, out int lastNonDefaultPathParameter,
+ out int lastNonNullablePathParameter)
+ {
+ // First step - loading defaults
+ for (int i = 0; i < this.pathSegmentVariableNames.Count; i++)
+ {
+ if (string.IsNullOrEmpty(normalizedParameters[i]) && (this.defaultValues != null))
+ {
+ this.defaultValues.TryGetValue(this.pathSegmentVariableNames[i], out normalizedParameters[i]);
+ }
+ }
+ // Second step - calculating bind constrains
+ lastNonDefaultPathParameter = this.pathSegmentVariableNames.Count - 1;
+ if ((this.defaultValues != null) &&
+ (this.owner.segments[this.owner.segments.Count - 1].Nature != UriTemplatePartType.Literal))
+ {
+ bool foundNonDefaultPathParameter = false;
+ while (!foundNonDefaultPathParameter && (lastNonDefaultPathParameter >= 0))
+ {
+ string defaultValue;
+ if (this.defaultValues.TryGetValue(this.pathSegmentVariableNames[lastNonDefaultPathParameter],
+ out defaultValue))
+ {
+ if (string.Compare(normalizedParameters[lastNonDefaultPathParameter],
+ defaultValue, StringComparison.Ordinal) != 0)
+ {
+ foundNonDefaultPathParameter = true;
+ }
+ else
+ {
+ lastNonDefaultPathParameter--;
+ }
+ }
+ else
+ {
+ foundNonDefaultPathParameter = true;
+ }
+ }
+ }
+ if (this.firstNullablePathVariable > lastNonDefaultPathParameter)
+ {
+ lastNonNullablePathParameter = this.firstNullablePathVariable - 1;
+ }
+ else
+ {
+ lastNonNullablePathParameter = lastNonDefaultPathParameter;
+ }
+ // Third step - validate
+ for (int i = 0; i <= lastNonNullablePathParameter; i++)
+ {
+ // Skip validation for terminating star variable segment :
+ if (this.owner.HasWildcard && this.owner.wildcard.HasVariable &&
+ (i == this.pathSegmentVariableNames.Count - 1))
+ {
+ continue;
+ }
+ // Validate
+ if (string.IsNullOrEmpty(normalizedParameters[i]))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("parameters",
+ SR.Format(SR.BindUriTemplateToNullOrEmptyPathParam, this.pathSegmentVariableNames[i]));
+ }
+ }
+ }
+ void ParseVariableDeclaration(string varDeclaration, out string varName, out string defaultValue)
+ {
+ if ((varDeclaration.IndexOf('{') != -1) || (varDeclaration.IndexOf('}') != -1))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidVarDeclaration, this.owner.originalTemplate, varDeclaration)));
+ }
+ int equalSignIndex = varDeclaration.IndexOf('=');
+ switch (equalSignIndex)
+ {
+ case -1:
+ varName = varDeclaration;
+ defaultValue = null;
+ break;
+
+ case 0:
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidVarDeclaration, this.owner.originalTemplate, varDeclaration)));
+
+ default:
+ varName = varDeclaration.Substring(0, equalSignIndex);
+ defaultValue = varDeclaration.Substring(equalSignIndex + 1);
+ if (defaultValue.IndexOf('=') != -1)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidVarDeclaration, this.owner.originalTemplate, varDeclaration)));
+ }
+ break;
+ }
+ }
+ string[] PrepareNormalizedParameters()
+ {
+ string[] normalizedParameters = new string[this.pathSegmentVariableNames.Count + this.queryValueVariableNames.Count];
+ for (int i = 0; i < normalizedParameters.Length; i++)
+ {
+ normalizedParameters[i] = null;
+ }
+ return normalizedParameters;
+ }
+ void ProcessBindParameter(string name, string value, string[] normalizedParameters,
+ ref IDictionary extraParameters)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("parameters",
+ SR.Format(SR.UTBindByNameCalledWithEmptyKey));
+ }
+
+ string uppercaseVarName = name.ToUpperInvariant();
+ int pathVarIndex = this.pathSegmentVariableNames.IndexOf(uppercaseVarName);
+ if (pathVarIndex != -1)
+ {
+ normalizedParameters[pathVarIndex] = (string.IsNullOrEmpty(value) ? string.Empty : value);
+ return;
+ }
+ int queryVarIndex = this.queryValueVariableNames.IndexOf(uppercaseVarName);
+ if (queryVarIndex != -1)
+ {
+ normalizedParameters[this.pathSegmentVariableNames.Count + queryVarIndex] = (string.IsNullOrEmpty(value) ? string.Empty : value);
+ return;
+ }
+ if (extraParameters == null)
+ {
+ extraParameters = new Dictionary(UriTemplateHelpers.GetQueryKeyComparer());
+ }
+ extraParameters.Add(name, value);
+ }
+ void ProcessDefaultsAndCreateBindInfo(bool omitDefaults, string[] normalizedParameters,
+ IDictionary extraParameters, out BindInformation bindInfo)
+ {
+ int lastNonDefaultPathParameter;
+ int lastNonNullablePathParameter;
+ LoadDefaultsAndValidate(normalizedParameters, out lastNonDefaultPathParameter,
+ out lastNonNullablePathParameter);
+ if (this.owner.additionalDefaults != null)
+ {
+ if (omitDefaults)
+ {
+ RemoveAdditionalDefaults(ref extraParameters);
+ }
+ else
+ {
+ AddAdditionalDefaults(ref extraParameters);
+ }
+ }
+ bindInfo = new BindInformation(normalizedParameters, lastNonDefaultPathParameter,
+ lastNonNullablePathParameter, extraParameters);
+ }
+ void RemoveAdditionalDefaults(ref IDictionary extraParameters)
+ {
+ if (extraParameters == null)
+ {
+ return;
+ }
+
+ foreach (KeyValuePair kvp in this.owner.additionalDefaults)
+ {
+ string extraParameter;
+ if (extraParameters.TryGetValue(kvp.Key, out extraParameter))
+ {
+ if (string.Compare(extraParameter, kvp.Value, StringComparison.Ordinal) == 0)
+ {
+ extraParameters.Remove(kvp.Key);
+ }
+ }
+ }
+ if (extraParameters.Count == 0)
+ {
+ extraParameters = null;
+ }
+ }
+ }
+
+ class WildcardInfo
+ {
+ readonly UriTemplate owner;
+ readonly string varName;
+
+ public WildcardInfo(UriTemplate owner)
+ {
+ this.varName = null;
+ this.owner = owner;
+ }
+ public WildcardInfo(UriTemplate owner, string segment)
+ {
+ Fx.Assert(!segment.EndsWith("/", StringComparison.Ordinal), "We are expecting to check this earlier");
+
+ bool hasDefault;
+ this.varName = owner.AddPathVariable(UriTemplatePartType.Variable,
+ segment.Substring(1 + WildcardPath.Length, segment.Length - 2 - WildcardPath.Length),
+ out hasDefault);
+ // Since this is a terminating star segment there shouldn't be a default
+ if (hasDefault)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTStarVariableWithDefaults, owner.originalTemplate,
+ segment, this.varName)));
+ }
+ this.owner = owner;
+ }
+
+ internal bool HasVariable
+ {
+ get
+ {
+ return (!string.IsNullOrEmpty(this.varName));
+ }
+ }
+
+ public void Bind(string[] values, ref int valueIndex, StringBuilder path)
+ {
+ if (HasVariable)
+ {
+ Fx.Assert(valueIndex < values.Length, "Not enough values to bind");
+ if (string.IsNullOrEmpty(values[valueIndex]))
+ {
+ valueIndex++;
+ }
+ else
+ {
+ path.Append(values[valueIndex++]);
+ }
+ }
+ }
+ public void Lookup(int numMatchedSegments, Collection relativePathSegments,
+ NameValueCollection boundParameters)
+ {
+ Fx.Assert(numMatchedSegments == this.owner.segments.Count, "We should have matched the other segments");
+ if (HasVariable)
+ {
+ StringBuilder remainingPath = new StringBuilder();
+ for (int i = numMatchedSegments; i < relativePathSegments.Count; i++)
+ {
+ if (i < relativePathSegments.Count - 1)
+ {
+ remainingPath.AppendFormat("{0}/", relativePathSegments[i]);
+ }
+ else
+ {
+ remainingPath.Append(relativePathSegments[i]);
+ }
+ }
+ boundParameters.Add(this.varName, remainingPath.ToString());
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateCompoundPathSegment.cs b/src/System.ServiceModel.Web/src/System/UriTemplateCompoundPathSegment.cs
new file mode 100644
index 00000000000..d20cdedc9d7
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateCompoundPathSegment.cs
@@ -0,0 +1,486 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Runtime;
+ using System.ServiceModel;
+ using System.Text;
+
+ // Thin wrapper around formatted string; use type system to help ensure we
+ // are doing canonicalization right/consistently - the literal sections are held in an
+ // un-escaped format
+ // We are assuming that the string will be always built as Lit{Var}Lit[{Var}Lit[{Var}Lit[...]]],
+ // when the first and last literals may be empty
+ class UriTemplateCompoundPathSegment : UriTemplatePathSegment, IComparable
+ {
+ readonly string firstLiteral;
+ readonly List varLitPairs;
+
+ CompoundSegmentClass csClass;
+
+ UriTemplateCompoundPathSegment(string originalSegment, bool endsWithSlash, string firstLiteral)
+ : base(originalSegment, UriTemplatePartType.Compound, endsWithSlash)
+ {
+ this.firstLiteral = firstLiteral;
+ this.varLitPairs = new List();
+ }
+ public static new UriTemplateCompoundPathSegment CreateFromUriTemplate(string segment, UriTemplate template)
+ {
+ string origSegment = segment;
+ bool endsWithSlash = segment.EndsWith("/", StringComparison.Ordinal);
+ if (endsWithSlash)
+ {
+ segment = segment.Remove(segment.Length - 1);
+ }
+
+ int nextVarStart = segment.IndexOf("{", StringComparison.Ordinal);
+ Fx.Assert(nextVarStart >= 0, "The method is only called after identifying a '{' character in the segment");
+ string firstLiteral = ((nextVarStart > 0) ? segment.Substring(0, nextVarStart) : string.Empty);
+ if (firstLiteral.IndexOf(UriTemplate.WildcardPath, StringComparison.Ordinal) != -1)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidWildcardInVariableOrLiteral, template.originalTemplate, UriTemplate.WildcardPath)));
+ }
+ UriTemplateCompoundPathSegment result = new UriTemplateCompoundPathSegment(origSegment, endsWithSlash,
+ ((firstLiteral != string.Empty) ? Uri.UnescapeDataString(firstLiteral) : string.Empty));
+ do
+ {
+ int nextVarEnd = segment.IndexOf("}", nextVarStart + 1, StringComparison.Ordinal);
+ if (nextVarEnd < nextVarStart + 2)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidFormatSegmentOrQueryPart, segment)));
+ }
+ bool hasDefault;
+ string varName = template.AddPathVariable(UriTemplatePartType.Compound,
+ segment.Substring(nextVarStart + 1, nextVarEnd - nextVarStart - 1), out hasDefault);
+ if (hasDefault)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTDefaultValueToCompoundSegmentVar, template, origSegment, varName)));
+ }
+ nextVarStart = segment.IndexOf("{", nextVarEnd + 1, StringComparison.Ordinal);
+ string literal;
+ if (nextVarStart > 0)
+ {
+ if (nextVarStart == nextVarEnd + 1)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("template",
+ SR.Format(SR.UTDoesNotSupportAdjacentVarsInCompoundSegment, template, segment));
+ }
+ literal = segment.Substring(nextVarEnd + 1, nextVarStart - nextVarEnd - 1);
+ }
+ else if (nextVarEnd + 1 < segment.Length)
+ {
+ literal = segment.Substring(nextVarEnd + 1);
+ }
+ else
+ {
+ literal = string.Empty;
+ }
+ if (literal.IndexOf(UriTemplate.WildcardPath, StringComparison.Ordinal) != -1)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidWildcardInVariableOrLiteral, template.originalTemplate, UriTemplate.WildcardPath)));
+ }
+ if (literal.IndexOf('}') != -1)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidFormatSegmentOrQueryPart, segment)));
+ }
+ result.varLitPairs.Add(new VarAndLitPair(varName, ((literal == string.Empty) ? string.Empty : Uri.UnescapeDataString(literal))));
+ } while (nextVarStart > 0);
+
+ if (string.IsNullOrEmpty(result.firstLiteral))
+ {
+ if (string.IsNullOrEmpty(result.varLitPairs[result.varLitPairs.Count - 1].Literal))
+ {
+ result.csClass = CompoundSegmentClass.HasNoPrefixNorSuffix;
+ }
+ else
+ {
+ result.csClass = CompoundSegmentClass.HasOnlySuffix;
+ }
+ }
+ else
+ {
+ if (string.IsNullOrEmpty(result.varLitPairs[result.varLitPairs.Count - 1].Literal))
+ {
+ result.csClass = CompoundSegmentClass.HasOnlyPrefix;
+ }
+ else
+ {
+ result.csClass = CompoundSegmentClass.HasPrefixAndSuffix;
+ }
+ }
+
+ return result;
+ }
+
+ public override void Bind(string[] values, ref int valueIndex, StringBuilder path)
+ {
+ Fx.Assert(valueIndex + this.varLitPairs.Count <= values.Length, "Not enough values to bind");
+ path.Append(this.firstLiteral);
+ for (int pairIndex = 0; pairIndex < this.varLitPairs.Count; pairIndex++)
+ {
+ path.Append(values[valueIndex++]);
+ path.Append(this.varLitPairs[pairIndex].Literal);
+ }
+ if (this.EndsWithSlash)
+ {
+ path.Append("/");
+ }
+ }
+ public override bool IsEquivalentTo(UriTemplatePathSegment other, bool ignoreTrailingSlash)
+ {
+ if (other == null)
+ {
+ Fx.Assert("why would we ever call this?");
+ return false;
+ }
+ if (!ignoreTrailingSlash && (this.EndsWithSlash != other.EndsWithSlash))
+ {
+ return false;
+ }
+ UriTemplateCompoundPathSegment otherAsCompound = other as UriTemplateCompoundPathSegment;
+ if (otherAsCompound == null)
+ {
+ // if other can't be cast as a compound then it can't be equivalent
+ return false;
+ }
+ if (this.varLitPairs.Count != otherAsCompound.varLitPairs.Count)
+ {
+ return false;
+ }
+ if (StringComparer.OrdinalIgnoreCase.Compare(this.firstLiteral, otherAsCompound.firstLiteral) != 0)
+ {
+ return false;
+ }
+ for (int pairIndex = 0; pairIndex < this.varLitPairs.Count; pairIndex++)
+ {
+ if (StringComparer.OrdinalIgnoreCase.Compare(this.varLitPairs[pairIndex].Literal,
+ otherAsCompound.varLitPairs[pairIndex].Literal) != 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ public override bool IsMatch(UriTemplateLiteralPathSegment segment, bool ignoreTrailingSlash)
+ {
+ if (!ignoreTrailingSlash && (this.EndsWithSlash != segment.EndsWithSlash))
+ {
+ return false;
+ }
+ return TryLookup(segment.AsUnescapedString(), null);
+ }
+ public override void Lookup(string segment, NameValueCollection boundParameters)
+ {
+ if (!TryLookup(segment, boundParameters))
+ {
+ Fx.Assert("How can that be? Lookup is expected to be called after IsMatch");
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTCSRLookupBeforeMatch)));
+ }
+ }
+
+ bool TryLookup(string segment, NameValueCollection boundParameters)
+ {
+ int segmentPosition = 0;
+ if (!string.IsNullOrEmpty(this.firstLiteral))
+ {
+ if (segment.StartsWith(this.firstLiteral, StringComparison.Ordinal))
+ {
+ segmentPosition = this.firstLiteral.Length;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ for (int pairIndex = 0; pairIndex < this.varLitPairs.Count - 1; pairIndex++)
+ {
+ int nextLiteralPosition = segment.IndexOf(this.varLitPairs[pairIndex].Literal, segmentPosition, StringComparison.Ordinal);
+ if (nextLiteralPosition < segmentPosition + 1)
+ {
+ return false;
+ }
+ if (boundParameters != null)
+ {
+ string varValue = segment.Substring(segmentPosition, nextLiteralPosition - segmentPosition);
+ boundParameters.Add(this.varLitPairs[pairIndex].VarName, varValue);
+ }
+ segmentPosition = nextLiteralPosition + this.varLitPairs[pairIndex].Literal.Length;
+ }
+ if (segmentPosition < segment.Length)
+ {
+ if (string.IsNullOrEmpty(this.varLitPairs[varLitPairs.Count - 1].Literal))
+ {
+ if (boundParameters != null)
+ {
+ boundParameters.Add(this.varLitPairs[varLitPairs.Count - 1].VarName,
+ segment.Substring(segmentPosition));
+ }
+ return true;
+ }
+ else if ((segmentPosition + this.varLitPairs[varLitPairs.Count - 1].Literal.Length < segment.Length) &&
+ segment.EndsWith(this.varLitPairs[varLitPairs.Count - 1].Literal, StringComparison.Ordinal))
+ {
+ if (boundParameters != null)
+ {
+ boundParameters.Add(this.varLitPairs[varLitPairs.Count - 1].VarName,
+ segment.Substring(segmentPosition, segment.Length - segmentPosition - this.varLitPairs[varLitPairs.Count - 1].Literal.Length));
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // A note about comparing compound segments:
+ // We are using this for generating the sorted collections at the nodes of the UriTemplateTrieNode.
+ // The idea is that we are sorting the segments based on preferred matching, when we have two
+ // compound segments matching the same wire segment, we will give preference to the preceding one.
+ // The order is based on the following concepts:
+ // - We are defining four classes of compound segments: prefix+suffix, prefix-only, suffix-only
+ // and none
+ // - Whenever we are comparing segments from different class the preferred one is the segment with
+ // the prefared class, based on the order we defined them (p+s \ p \ s \ n).
+ // - Within each class the preference is based on the prefix\suffix, while prefix has precedence
+ // over suffix if both exists.
+ // - If after comparing the class, as well as the prefix\suffix, we didn't reach to a conclusion,
+ // the preference is given to the segment with more variables parts.
+ // This order mostly follows the intuitive common sense; the major issue comes from preferring the
+ // prefix over the suffix in the case where both exist. This is derived from the problematic of any
+ // other type of solution that don't prefere the prefix over the suffix or vice versa. To better
+ // understanding lets considered the following example:
+ // In comparing 'foo{x}bar' and 'food{x}ar', unless we are preferring prefix or suffix, we have
+ // to state that they have the same order. So is the case with 'foo{x}babar' and 'food{x}ar', which
+ // will lead us to claiming the 'foo{x}bar' and 'foo{x}babar' are from the same order, which they
+ // clearly are not.
+ // Taking other approaches to this problem results in similar cases. The only solution is preferring
+ // either the prefix or the suffix over the other; since we already preferred prefix over suffix
+ // implicitly (we preferred the prefix only class over the suffix only, we also prefared literal
+ // over variable, if in the same path segment) that still maintain consistency.
+ // Therefore:
+ // - 'food{var}' should be before 'foo{var}'; '{x}.{y}.{z}' should be before '{x}.{y}'.
+ // - the order between '{var}bar' and '{var}qux' is not important
+ // - '{x}.{y}' and '{x}_{y}' should have the same order
+ // - 'foo{x}bar' is less preferred than 'food{x}ar'
+ // In the above third case - if we are opening the table with allowDuplicate=false, we will throw;
+ // if we are opening it with allowDuplicate=true we will let it go and might match both templates
+ // for certain wire candidates.
+ int IComparable.CompareTo(UriTemplateCompoundPathSegment other)
+ {
+ Fx.Assert(other != null, "We are only expected to get here for comparing real compound segments");
+
+ switch (this.csClass)
+ {
+ case CompoundSegmentClass.HasPrefixAndSuffix:
+ switch (other.csClass)
+ {
+ case CompoundSegmentClass.HasPrefixAndSuffix:
+ return CompareToOtherThatHasPrefixAndSuffix(other);
+
+ case CompoundSegmentClass.HasOnlyPrefix:
+ case CompoundSegmentClass.HasOnlySuffix:
+ case CompoundSegmentClass.HasNoPrefixNorSuffix:
+ return -1;
+
+ default:
+ Fx.Assert("Invalid other.CompoundSegmentClass");
+ return 0;
+ }
+
+ case CompoundSegmentClass.HasOnlyPrefix:
+ switch (other.csClass)
+ {
+ case CompoundSegmentClass.HasPrefixAndSuffix:
+ return 1;
+
+ case CompoundSegmentClass.HasOnlyPrefix:
+ return CompareToOtherThatHasOnlyPrefix(other);
+
+ case CompoundSegmentClass.HasOnlySuffix:
+ case CompoundSegmentClass.HasNoPrefixNorSuffix:
+ return -1;
+
+ default:
+ Fx.Assert("Invalid other.CompoundSegmentClass");
+ return 0;
+ }
+
+ case CompoundSegmentClass.HasOnlySuffix:
+ switch (other.csClass)
+ {
+ case CompoundSegmentClass.HasPrefixAndSuffix:
+ case CompoundSegmentClass.HasOnlyPrefix:
+ return 1;
+
+ case CompoundSegmentClass.HasOnlySuffix:
+ return CompareToOtherThatHasOnlySuffix(other);
+
+ case CompoundSegmentClass.HasNoPrefixNorSuffix:
+ return -1;
+
+ default:
+ Fx.Assert("Invalid other.CompoundSegmentClass");
+ return 0;
+ }
+
+ case CompoundSegmentClass.HasNoPrefixNorSuffix:
+ switch (other.csClass)
+ {
+ case CompoundSegmentClass.HasPrefixAndSuffix:
+ case CompoundSegmentClass.HasOnlyPrefix:
+ case CompoundSegmentClass.HasOnlySuffix:
+ return 1;
+
+ case CompoundSegmentClass.HasNoPrefixNorSuffix:
+ return CompareToOtherThatHasNoPrefixNorSuffix(other);
+
+ default:
+ Fx.Assert("Invalid other.CompoundSegmentClass");
+ return 0;
+ }
+
+ default:
+ Fx.Assert("Invalid this.CompoundSegmentClass");
+ return 0;
+ }
+ }
+ int CompareToOtherThatHasPrefixAndSuffix(UriTemplateCompoundPathSegment other)
+ {
+ Fx.Assert(this.csClass == CompoundSegmentClass.HasPrefixAndSuffix, "Otherwise, how did we got here?");
+ Fx.Assert(other.csClass == CompoundSegmentClass.HasPrefixAndSuffix, "Otherwise, how did we got here?");
+
+ // In this case we are determining the order based on the prefix of the two segments,
+ // then by their suffix and then based on the number of variables
+ int prefixOrder = ComparePrefixToOtherPrefix(other);
+ if (prefixOrder == 0)
+ {
+ int suffixOrder = CompareSuffixToOtherSuffix(other);
+ if (suffixOrder == 0)
+ {
+ return (other.varLitPairs.Count - this.varLitPairs.Count);
+ }
+ else
+ {
+ return suffixOrder;
+ }
+ }
+ else
+ {
+ return prefixOrder;
+ }
+ }
+ int CompareToOtherThatHasOnlyPrefix(UriTemplateCompoundPathSegment other)
+ {
+ Fx.Assert(this.csClass == CompoundSegmentClass.HasOnlyPrefix, "Otherwise, how did we got here?");
+ Fx.Assert(other.csClass == CompoundSegmentClass.HasOnlyPrefix, "Otherwise, how did we got here?");
+
+ // In this case we are determining the order based on the prefix of the two segments,
+ // then based on the number of variables
+ int prefixOrder = ComparePrefixToOtherPrefix(other);
+ if (prefixOrder == 0)
+ {
+ return (other.varLitPairs.Count - this.varLitPairs.Count);
+ }
+ else
+ {
+ return prefixOrder;
+ }
+ }
+ int CompareToOtherThatHasOnlySuffix(UriTemplateCompoundPathSegment other)
+ {
+ Fx.Assert(this.csClass == CompoundSegmentClass.HasOnlySuffix, "Otherwise, how did we got here?");
+ Fx.Assert(other.csClass == CompoundSegmentClass.HasOnlySuffix, "Otherwise, how did we got here?");
+
+ // In this case we are determining the order based on the suffix of the two segments,
+ // then based on the number of variables
+ int suffixOrder = CompareSuffixToOtherSuffix(other);
+ if (suffixOrder == 0)
+ {
+ return (other.varLitPairs.Count - this.varLitPairs.Count);
+ }
+ else
+ {
+ return suffixOrder;
+ }
+ }
+ int CompareToOtherThatHasNoPrefixNorSuffix(UriTemplateCompoundPathSegment other)
+ {
+ Fx.Assert(this.csClass == CompoundSegmentClass.HasNoPrefixNorSuffix, "Otherwise, how did we got here?");
+ Fx.Assert(other.csClass == CompoundSegmentClass.HasNoPrefixNorSuffix, "Otherwise, how did we got here?");
+
+ // In this case the order is determined by the number of variables
+ return (other.varLitPairs.Count - this.varLitPairs.Count);
+ }
+ int ComparePrefixToOtherPrefix(UriTemplateCompoundPathSegment other)
+ {
+ return string.Compare(other.firstLiteral, this.firstLiteral, StringComparison.OrdinalIgnoreCase);
+ }
+ int CompareSuffixToOtherSuffix(UriTemplateCompoundPathSegment other)
+ {
+ string reversedSuffix = ReverseString(this.varLitPairs[this.varLitPairs.Count - 1].Literal);
+ string reversedOtherSuffix = ReverseString(other.varLitPairs[other.varLitPairs.Count - 1].Literal);
+ return string.Compare(reversedOtherSuffix, reversedSuffix, StringComparison.OrdinalIgnoreCase);
+ }
+ static string ReverseString(string stringToReverse)
+ {
+ char[] reversedString = new char[stringToReverse.Length];
+ for (int i = 0; i < stringToReverse.Length; i++)
+ {
+ reversedString[i] = stringToReverse[stringToReverse.Length - i - 1];
+ }
+ return new string(reversedString);
+ }
+
+ enum CompoundSegmentClass
+ {
+ Undefined,
+ HasPrefixAndSuffix,
+ HasOnlyPrefix,
+ HasOnlySuffix,
+ HasNoPrefixNorSuffix
+ }
+
+ struct VarAndLitPair
+ {
+ readonly string literal;
+ readonly string varName;
+
+ public VarAndLitPair(string varName, string literal)
+ {
+ this.varName = varName;
+ this.literal = literal;
+ }
+
+ public string Literal
+ {
+ get
+ {
+ return this.literal;
+ }
+ }
+ public string VarName
+ {
+ get
+ {
+ return this.varName;
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateEquivalenceComparer.cs b/src/System.ServiceModel.Web/src/System/UriTemplateEquivalenceComparer.cs
new file mode 100644
index 00000000000..09ffb36296e
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateEquivalenceComparer.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings (for Presharp)
+
+namespace System
+{
+ using System.Collections.Generic;
+ using System.ServiceModel;
+ using System.Runtime.CompilerServices;
+
+ [TypeForwardedFrom("System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
+ public class UriTemplateEquivalenceComparer : IEqualityComparer
+ {
+ static UriTemplateEquivalenceComparer instance;
+ internal static UriTemplateEquivalenceComparer Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ // lock-free, fine if we allocate more than one
+ instance = new UriTemplateEquivalenceComparer();
+ }
+ return instance;
+ }
+ }
+
+ public bool Equals(UriTemplate x, UriTemplate y)
+ {
+ if (x == null)
+ {
+ return y == null;
+ }
+ return x.IsEquivalentTo(y);
+ }
+ public int GetHashCode(UriTemplate obj)
+ {
+ if (obj == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("obj");
+ }
+#pragma warning disable 56506 // obj.xxx is never null
+ // prefer final literal segment (common literal prefixes are common in some scenarios)
+ for (int i = obj.segments.Count - 1; i >= 0; --i)
+ {
+ if (obj.segments[i].Nature == UriTemplatePartType.Literal)
+ {
+ return obj.segments[i].GetHashCode();
+ }
+ }
+ return obj.segments.Count + obj.queries.Count;
+#pragma warning restore 56506
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateHelpers.cs b/src/System.ServiceModel.Web/src/System/UriTemplateHelpers.cs
new file mode 100644
index 00000000000..f708b32031e
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateHelpers.cs
@@ -0,0 +1,387 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Runtime;
+ using System.ServiceModel;
+ using System.ServiceModel.Channels;
+
+ static class UriTemplateHelpers
+ {
+ static UriTemplateQueryComparer queryComparer = new UriTemplateQueryComparer();
+ static UriTemplateQueryKeyComparer queryKeyComperar = new UriTemplateQueryKeyComparer();
+
+ [Conditional("DEBUG")]
+ public static void AssertCanonical(string s)
+ {
+ Fx.Assert(s == s.ToUpperInvariant(), "non-canonicalized");
+ }
+ public static bool CanMatchQueryInterestingly(UriTemplate ut, NameValueCollection query, bool mustBeEspeciallyInteresting)
+ {
+ if (ut.queries.Count == 0)
+ {
+ return false; // trivial, not interesting
+ }
+ string[] queryKeys = query.AllKeys;
+ foreach (KeyValuePair kvp in ut.queries)
+ {
+ string queryKeyName = kvp.Key;
+ if (kvp.Value.Nature == UriTemplatePartType.Literal)
+ {
+ bool queryKeysContainsQueryVarName = false;
+ for (int i = 0; i < queryKeys.Length; ++i)
+ {
+ if (StringComparer.OrdinalIgnoreCase.Equals(queryKeys[i], queryKeyName))
+ {
+ queryKeysContainsQueryVarName = true;
+ break;
+ }
+ }
+ if (!queryKeysContainsQueryVarName)
+ {
+ return false;
+ }
+ if (kvp.Value == UriTemplateQueryValue.Empty)
+ {
+ if (!string.IsNullOrEmpty(query[queryKeyName]))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (((UriTemplateLiteralQueryValue)(kvp.Value)).AsRawUnescapedString() != query[queryKeyName])
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (mustBeEspeciallyInteresting && Array.IndexOf(queryKeys, queryKeyName) == -1)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public static bool CanMatchQueryTrivially(UriTemplate ut)
+ {
+ return (ut.queries.Count == 0);
+ }
+
+ public static void DisambiguateSamePath(UriTemplate[] array, int a, int b, bool allowDuplicateEquivalentUriTemplates)
+ {
+ // [a,b) all have same path
+ // ensure queries make them unambiguous
+ Fx.Assert(b > a, "array bug");
+ // sort empty queries to front
+ Array.Sort(array, a, b - a, queryComparer);
+ if (b - a == 1)
+ {
+ return; // if only one, cannot be ambiguous
+ }
+ if (!allowDuplicateEquivalentUriTemplates)
+ {
+ // ensure at most one empty query and ignore it
+ if (array[a].queries.Count == 0)
+ {
+ a++;
+ }
+ if (array[a].queries.Count == 0)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTTDuplicate, array[a].ToString(), array[a - 1].ToString())));
+ }
+ if (b - a == 1)
+ {
+ return; // if only one, cannot be ambiguous
+ }
+ }
+ else
+ {
+ while (a < b && array[a].queries.Count == 0) // all equivalent
+ {
+ a++;
+ }
+ if (b - a <= 1)
+ {
+ return;
+ }
+ }
+ Fx.Assert(b > a, "array bug");
+ // now consider non-empty queries
+ // more than one, so enforce that
+ // forall
+ // exist set of querystringvars S where
+ // every op has literal value foreach var in S, and
+ // those literal tuples are different
+ EnsureQueriesAreDistinct(array, a, b, allowDuplicateEquivalentUriTemplates);
+ }
+
+ public static IEqualityComparer GetQueryKeyComparer()
+ {
+ return queryKeyComperar;
+ }
+
+ public static string GetUriPath(Uri uri)
+ {
+ return uri.GetComponents(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.Unescaped);
+ }
+ public static bool HasQueryLiteralRequirements(UriTemplate ut)
+ {
+ foreach (UriTemplateQueryValue utqv in ut.queries.Values)
+ {
+ if (utqv.Nature == UriTemplatePartType.Literal)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static UriTemplatePartType IdentifyPartType(string part)
+ {
+ // Identifying the nature of a string - Literal|Compound|Variable
+ // Algorithem is based on the following steps:
+ // - Finding the position of the first open curlly brace ('{') and close curlly brace ('}')
+ // in the string
+ // - If we don't find any this is a Literal
+ // - otherwise, we validate that position of the close brace is at least two characters from
+ // the position of the open brace
+ // - Then we identify if we are dealing with a compound string or a single variable string
+ // + var name is not at the string start --> Compound
+ // + var name is shorter then the entire string (End < Length-2 or End==Length-2
+ // and string ends with '/') --> Compound
+ // + otherwise --> Variable
+ int varStartIndex = part.IndexOf("{", StringComparison.Ordinal);
+ int varEndIndex = part.IndexOf("}", StringComparison.Ordinal);
+ if (varStartIndex == -1)
+ {
+ if (varEndIndex != -1)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidFormatSegmentOrQueryPart, part)));
+ }
+ return UriTemplatePartType.Literal;
+ }
+ else
+ {
+ if (varEndIndex < varStartIndex + 2)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidFormatSegmentOrQueryPart, part)));
+ }
+ if (varStartIndex > 0)
+ {
+ return UriTemplatePartType.Compound;
+ }
+ else if ((varEndIndex < part.Length - 2) ||
+ ((varEndIndex == part.Length - 2) && !part.EndsWith("/", StringComparison.Ordinal)))
+ {
+ return UriTemplatePartType.Compound;
+ }
+ else
+ {
+ return UriTemplatePartType.Variable;
+ }
+ }
+ }
+ public static bool IsWildcardPath(string path)
+ {
+ if (path.IndexOf('/') != -1)
+ {
+ return false;
+ }
+ UriTemplatePartType partType;
+ return IsWildcardSegment(path, out partType);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is internal method that needs to return multiple things")]
+ public static bool IsWildcardSegment(string segment, out UriTemplatePartType type)
+ {
+ type = IdentifyPartType(segment);
+ switch (type)
+ {
+ case UriTemplatePartType.Literal:
+ return (string.Compare(segment, UriTemplate.WildcardPath, StringComparison.Ordinal) == 0);
+
+ case UriTemplatePartType.Compound:
+ return false;
+
+ case UriTemplatePartType.Variable:
+ return ((segment.IndexOf(UriTemplate.WildcardPath, StringComparison.Ordinal) == 1) &&
+ !segment.EndsWith("/", StringComparison.Ordinal) &&
+ (segment.Length > UriTemplate.WildcardPath.Length + 2));
+
+ default:
+ Fx.Assert("Bad part type identification !");
+ return false;
+ }
+ }
+ public static NameValueCollection ParseQueryString(string query)
+ {
+ // We are adjusting the parsing of UrlUtility.ParseQueryString, which identify
+ // ?wsdl as a null key with wsdl as a value
+ NameValueCollection result = UrlUtility.ParseQueryString(query);
+ string nullKeyValuesString = result[(string)null];
+ if (!string.IsNullOrEmpty(nullKeyValuesString))
+ {
+ result.Remove(null);
+ string[] nullKeyValues = nullKeyValuesString.Split(',');
+ for (int i = 0; i < nullKeyValues.Length; i++)
+ {
+ result.Add(nullKeyValues[i], null);
+ }
+ }
+ return result;
+ }
+ static bool AllTemplatesAreEquivalent(IList array, int a, int b)
+ {
+ for (int i = a; i < b - 1; ++i)
+ {
+ if (!array[i].IsEquivalentTo(array[i + 1]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static void EnsureQueriesAreDistinct(UriTemplate[] array, int a, int b, bool allowDuplicateEquivalentUriTemplates)
+ {
+ Dictionary queryVarNamesWithLiteralVals = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ for (int i = a; i < b; ++i)
+ {
+ foreach (KeyValuePair kvp in array[i].queries)
+ {
+ if (kvp.Value.Nature == UriTemplatePartType.Literal)
+ {
+ if (!queryVarNamesWithLiteralVals.ContainsKey(kvp.Key))
+ {
+ queryVarNamesWithLiteralVals.Add(kvp.Key, 0);
+ }
+ }
+ }
+ }
+ // now we have set of possibilities:
+ // further refine to only those for whom all templates have literals
+ Dictionary queryVarNamesAllLiterals = new Dictionary(queryVarNamesWithLiteralVals);
+ for (int i = a; i < b; ++i)
+ {
+ foreach (string s in queryVarNamesWithLiteralVals.Keys)
+ {
+ if (!array[i].queries.ContainsKey(s) || (array[i].queries[s].Nature != UriTemplatePartType.Literal))
+ {
+ queryVarNamesAllLiterals.Remove(s);
+ }
+ }
+ }
+ queryVarNamesWithLiteralVals = null; // ensure we don't reference this variable any more
+ // now we have the set of names that every operation has as a literal
+ if (queryVarNamesAllLiterals.Count == 0)
+ {
+ if (allowDuplicateEquivalentUriTemplates && AllTemplatesAreEquivalent(array, a, b))
+ {
+ // we're ok, do nothing
+ }
+ else
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTTOtherAmbiguousQueries, array[a].ToString())));
+ }
+ }
+ // now just ensure that each template has a unique tuple of values for the names
+ string[][] upsLits = new string[b - a][];
+ for (int i = 0; i < b - a; ++i)
+ {
+ upsLits[i] = GetQueryLiterals(array[i + a], queryVarNamesAllLiterals);
+ }
+ for (int i = 0; i < b - a; ++i)
+ {
+ for (int j = i + 1; j < b - a; ++j)
+ {
+ if (Same(upsLits[i], upsLits[j]))
+ {
+ if (!array[i + a].IsEquivalentTo(array[j + a]))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTTAmbiguousQueries, array[a + i].ToString(), array[j + a].ToString())));
+ }
+ Fx.Assert(array[i + a].IsEquivalentTo(array[j + a]), "bad equiv logic");
+ if (!allowDuplicateEquivalentUriTemplates)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTTDuplicate, array[a + i].ToString(), array[j + a].ToString())));
+ }
+ }
+ }
+ }
+ // we're good. whew!
+ }
+ static string[] GetQueryLiterals(UriTemplate up, Dictionary queryVarNames)
+ {
+ string[] queryLitVals = new string[queryVarNames.Count];
+ int i = 0;
+ foreach (string queryVarName in queryVarNames.Keys)
+ {
+ Fx.Assert(up.queries.ContainsKey(queryVarName), "query doesn't have name");
+ UriTemplateQueryValue utqv = up.queries[queryVarName];
+ Fx.Assert(utqv.Nature == UriTemplatePartType.Literal, "query for name is not literal");
+ if (utqv == UriTemplateQueryValue.Empty)
+ {
+ queryLitVals[i] = null;
+ }
+ else
+ {
+ queryLitVals[i] = ((UriTemplateLiteralQueryValue)(utqv)).AsRawUnescapedString();
+ }
+ ++i;
+ }
+ return queryLitVals;
+ }
+ static bool Same(string[] a, string[] b)
+ {
+ Fx.Assert(a.Length == b.Length, "arrays not same length");
+ for (int i = 0; i < a.Length; ++i)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ class UriTemplateQueryComparer : IComparer
+ {
+ public int Compare(UriTemplate x, UriTemplate y)
+ {
+ // sort the empty queries to the front
+ return Comparer.Default.Compare(x.queries.Count, y.queries.Count);
+ }
+ }
+ class UriTemplateQueryKeyComparer : IEqualityComparer
+ {
+ public bool Equals(string x, string y)
+ {
+ return (string.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0);
+ }
+ public int GetHashCode(string obj)
+ {
+ if (obj == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("obj");
+ }
+ return obj.ToUpperInvariant().GetHashCode();
+ }
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateLiteralPathSegment.cs b/src/System.ServiceModel.Web/src/System/UriTemplateLiteralPathSegment.cs
new file mode 100644
index 00000000000..e582903c46b
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateLiteralPathSegment.cs
@@ -0,0 +1,142 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Specialized;
+ using System.Runtime;
+ using System.ServiceModel;
+ using System.Text;
+
+ // thin wrapper around string; use type system to help ensure we
+ // are doing canonicalization right/consistently
+ class UriTemplateLiteralPathSegment : UriTemplatePathSegment, IComparable
+ {
+
+ // segment doesn't store trailing slash
+ readonly string segment;
+ static Uri dummyUri = new Uri("http://localhost");
+
+ UriTemplateLiteralPathSegment(string segment)
+ : base(segment, UriTemplatePartType.Literal, segment.EndsWith("/", StringComparison.Ordinal))
+ {
+ Fx.Assert(segment != null, "bad literal segment");
+ if (this.EndsWithSlash)
+ {
+ this.segment = segment.Remove(segment.Length - 1);
+ }
+ else
+ {
+ this.segment = segment;
+ }
+ }
+ public static new UriTemplateLiteralPathSegment CreateFromUriTemplate(string segment, UriTemplate template)
+ {
+ // run it through UriBuilder to escape-if-necessary it
+ if (string.Compare(segment, "/", StringComparison.Ordinal) == 0)
+ {
+ // running an empty segment through UriBuilder has unexpected/wrong results
+ return new UriTemplateLiteralPathSegment("/");
+ }
+ if (segment.IndexOf(UriTemplate.WildcardPath, StringComparison.Ordinal) != -1)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
+ SR.Format(SR.UTInvalidWildcardInVariableOrLiteral, template.originalTemplate, UriTemplate.WildcardPath)));
+ }
+ // '*' is not usually escaped by the Uri\UriBuilder to %2a, since we forbid passing a
+ // clear character and the workaroud is to pass the escaped form, we should replace the
+ // escaped form with the regular one.
+ segment = segment.Replace("%2a", "*").Replace("%2A", "*");
+ UriBuilder ub = new UriBuilder(dummyUri);
+ ub.Path = segment;
+ string escapedIfNecessarySegment = ub.Uri.AbsolutePath.Substring(1);
+ if (escapedIfNecessarySegment == string.Empty)
+ {
+ // This path through UriBuilder will sometimes '----' various segments
+ // such as '../' and './'. When this happens and the result is an empty
+ // string, we should just throw and tell the user we don't handle that.
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("segment",
+ SR.Format(SR.UTInvalidFormatSegmentOrQueryPart, segment));
+ }
+ return new UriTemplateLiteralPathSegment(escapedIfNecessarySegment);
+ }
+ public static UriTemplateLiteralPathSegment CreateFromWireData(string segment)
+ {
+ return new UriTemplateLiteralPathSegment(segment);
+ }
+
+ public string AsUnescapedString()
+ {
+ Fx.Assert(this.segment != null, "this should only be called by Bind\\Lookup");
+ return Uri.UnescapeDataString(this.segment);
+ }
+ public override void Bind(string[] values, ref int valueIndex, StringBuilder path)
+ {
+ if (this.EndsWithSlash)
+ {
+ path.AppendFormat("{0}/", AsUnescapedString());
+ }
+ else
+ {
+ path.Append(AsUnescapedString());
+ }
+ }
+
+ public int CompareTo(UriTemplateLiteralPathSegment other)
+ {
+ return StringComparer.OrdinalIgnoreCase.Compare(this.segment, other.segment);
+ }
+
+ public override bool Equals(object obj)
+ {
+ UriTemplateLiteralPathSegment lps = obj as UriTemplateLiteralPathSegment;
+ if (lps == null)
+ {
+ Fx.Assert("why would we ever call this?");
+ return false;
+ }
+ else
+ {
+ return ((this.EndsWithSlash == lps.EndsWithSlash) &&
+ StringComparer.OrdinalIgnoreCase.Equals(this.segment, lps.segment));
+ }
+ }
+ public override int GetHashCode()
+ {
+ return StringComparer.OrdinalIgnoreCase.GetHashCode(this.segment);
+ }
+
+ public override bool IsEquivalentTo(UriTemplatePathSegment other, bool ignoreTrailingSlash)
+ {
+ if (other == null)
+ {
+ Fx.Assert("why would we ever call this?");
+ return false;
+ }
+ if (other.Nature != UriTemplatePartType.Literal)
+ {
+ return false;
+ }
+ UriTemplateLiteralPathSegment otherAsLiteral = other as UriTemplateLiteralPathSegment;
+ Fx.Assert(otherAsLiteral != null, "The nature requires that this will be OK");
+ return IsMatch(otherAsLiteral, ignoreTrailingSlash);
+ }
+ public override bool IsMatch(UriTemplateLiteralPathSegment segment, bool ignoreTrailingSlash)
+ {
+ if (!ignoreTrailingSlash && (segment.EndsWithSlash != this.EndsWithSlash))
+ {
+ return false;
+ }
+ return (CompareTo(segment) == 0);
+ }
+ public bool IsNullOrEmpty()
+ {
+ return string.IsNullOrEmpty(this.segment);
+ }
+ public override void Lookup(string segment, NameValueCollection boundParameters)
+ {
+ Fx.Assert(StringComparer.OrdinalIgnoreCase.Compare(AsUnescapedString(), segment) == 0,
+ "How can that be? Lookup is expected to be called after IsMatch");
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateLiteralQueryValue.cs b/src/System.ServiceModel.Web/src/System/UriTemplateLiteralQueryValue.cs
new file mode 100644
index 00000000000..6c8a8944491
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateLiteralQueryValue.cs
@@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Specialized;
+ using System.Runtime;
+ using System.ServiceModel.Channels;
+ using System.Text;
+
+ // thin wrapper around string; use type system to help ensure we
+ // are doing canonicalization right/consistently
+ class UriTemplateLiteralQueryValue : UriTemplateQueryValue, IComparable
+ {
+ readonly string value; // an unescaped representation
+
+ UriTemplateLiteralQueryValue(string value)
+ : base(UriTemplatePartType.Literal)
+ {
+ Fx.Assert(value != null, "bad literal value");
+ this.value = value;
+ }
+ public static UriTemplateLiteralQueryValue CreateFromUriTemplate(string value)
+ {
+ return new UriTemplateLiteralQueryValue(UrlUtility.UrlDecode(value, Encoding.UTF8));
+ }
+
+ public string AsEscapedString()
+ {
+ return UrlUtility.UrlEncode(this.value, Encoding.UTF8);
+ }
+ public string AsRawUnescapedString()
+ {
+ return this.value;
+ }
+ public override void Bind(string keyName, string[] values, ref int valueIndex, StringBuilder query)
+ {
+ query.AppendFormat("&{0}={1}", UrlUtility.UrlEncode(keyName, Encoding.UTF8), AsEscapedString());
+ }
+
+ public int CompareTo(UriTemplateLiteralQueryValue other)
+ {
+ return string.Compare(this.value, other.value, StringComparison.Ordinal);
+ }
+
+ public override bool Equals(object obj)
+ {
+ UriTemplateLiteralQueryValue lqv = obj as UriTemplateLiteralQueryValue;
+ if (lqv == null)
+ {
+ Fx.Assert("why would we ever call this?");
+ return false;
+ }
+ else
+ {
+ return this.value == lqv.value;
+ }
+ }
+ public override int GetHashCode()
+ {
+ return this.value.GetHashCode();
+ }
+
+ public override bool IsEquivalentTo(UriTemplateQueryValue other)
+ {
+ if (other == null)
+ {
+ Fx.Assert("why would we ever call this?");
+ return false;
+ }
+ if (other.Nature != UriTemplatePartType.Literal)
+ {
+ return false;
+ }
+ UriTemplateLiteralQueryValue otherAsLiteral = other as UriTemplateLiteralQueryValue;
+ Fx.Assert(otherAsLiteral != null, "The nature requires that this will be OK");
+ return (CompareTo(otherAsLiteral) == 0);
+ }
+ public override void Lookup(string value, NameValueCollection boundParameters)
+ {
+ Fx.Assert(string.Compare(this.value, value, StringComparison.Ordinal) == 0, "How can that be?");
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateMatch.cs b/src/System.ServiceModel.Web/src/System/UriTemplateMatch.cs
new file mode 100644
index 00000000000..a8ad33e25ec
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateMatch.cs
@@ -0,0 +1,176 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.ObjectModel;
+ using System.Collections.Specialized;
+ using System.Runtime;
+ using System.Runtime.CompilerServices;
+ using System.ServiceModel.Channels;
+ using System.Net;
+
+ [TypeForwardedFrom("System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
+ public class UriTemplateMatch
+ {
+ Uri baseUri;
+ NameValueCollection boundVariables;
+ object data;
+ NameValueCollection queryParameters;
+ Collection relativePathSegments;
+ Uri requestUri;
+ UriTemplate template;
+ Collection wildcardPathSegments;
+ int wildcardSegmentsStartOffset = -1;
+ Uri originalBaseUri;
+ HttpRequestMessageProperty requestProp;
+
+ public UriTemplateMatch()
+ {
+ }
+
+ public Uri BaseUri // the base address, untouched
+ {
+ get
+ {
+ if (this.baseUri == null && this.originalBaseUri != null)
+ {
+ this.baseUri = UriTemplate.RewriteUri(this.originalBaseUri, this.requestProp.Headers[HttpRequestHeader.Host]);
+ }
+ return this.baseUri;
+ }
+ set
+ {
+ this.baseUri = value;
+ this.originalBaseUri = null;
+ this.requestProp = null;
+ }
+ }
+ public NameValueCollection BoundVariables // result of TryLookup, values are decoded
+ {
+ get
+ {
+ if (this.boundVariables == null)
+ {
+ this.boundVariables = new NameValueCollection();
+ }
+ return this.boundVariables;
+ }
+ }
+ public object Data
+ {
+ get
+ {
+ return this.data;
+ }
+ set
+ {
+ this.data = value;
+ }
+ }
+ public NameValueCollection QueryParameters // the result of UrlUtility.ParseQueryString (keys and values are decoded)
+ {
+ get
+ {
+ if (this.queryParameters == null)
+ {
+ PopulateQueryParameters();
+ }
+ return this.queryParameters;
+ }
+ }
+ public Collection RelativePathSegments // entire Path (after the base address), decoded
+ {
+ get
+ {
+ if (this.relativePathSegments == null)
+ {
+ this.relativePathSegments = new Collection();
+ }
+ return this.relativePathSegments;
+ }
+ }
+ public Uri RequestUri // uri on the wire, untouched
+ {
+ get
+ {
+ return this.requestUri;
+ }
+ set
+ {
+ this.requestUri = value;
+ }
+ }
+ public UriTemplate Template // which one got matched
+ {
+ get
+ {
+ return this.template;
+ }
+ set
+ {
+ this.template = value;
+ }
+ }
+ public Collection WildcardPathSegments // just the Path part matched by "*", decoded
+ {
+ get
+ {
+ if (this.wildcardPathSegments == null)
+ {
+ PopulateWildcardSegments();
+ }
+ return this.wildcardPathSegments;
+ }
+ }
+
+ internal void SetQueryParameters(NameValueCollection queryParameters)
+ {
+ this.queryParameters = new NameValueCollection(queryParameters);
+ }
+ internal void SetRelativePathSegments(Collection segments)
+ {
+ Fx.Assert(segments != null, "segments != null");
+ this.relativePathSegments = segments;
+ }
+ internal void SetWildcardPathSegmentsStart(int startOffset)
+ {
+ Fx.Assert(startOffset >= 0, "startOffset >= 0");
+ this.wildcardSegmentsStartOffset = startOffset;
+ }
+
+ internal void SetBaseUri(Uri originalBaseUri, HttpRequestMessageProperty requestProp)
+ {
+ this.baseUri = null;
+ this.originalBaseUri = originalBaseUri;
+ this.requestProp = requestProp;
+ }
+
+ void PopulateQueryParameters()
+ {
+ if (this.requestUri != null)
+ {
+ this.queryParameters = UriTemplateHelpers.ParseQueryString(this.requestUri.Query);
+ }
+ else
+ {
+ this.queryParameters = new NameValueCollection();
+ }
+ }
+ void PopulateWildcardSegments()
+ {
+ if (wildcardSegmentsStartOffset != -1)
+ {
+ this.wildcardPathSegments = new Collection();
+ for (int i = this.wildcardSegmentsStartOffset; i < this.RelativePathSegments.Count; ++i)
+ {
+ this.wildcardPathSegments.Add(this.RelativePathSegments[i]);
+ }
+ }
+ else
+ {
+ this.wildcardPathSegments = new Collection();
+ }
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateMatchException.cs b/src/System.ServiceModel.Web/src/System/UriTemplateMatchException.cs
new file mode 100644
index 00000000000..f0d20acdb75
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateMatchException.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Runtime.Serialization;
+ using System.Runtime.CompilerServices;
+
+ [TypeForwardedFrom("System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
+ [Serializable]
+ public class UriTemplateMatchException : SystemException
+ {
+ public UriTemplateMatchException()
+ {
+ }
+ public UriTemplateMatchException(string message)
+ : base(message)
+ {
+ }
+ public UriTemplateMatchException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+ protected UriTemplateMatchException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplatePartType.cs b/src/System.ServiceModel.Web/src/System/UriTemplatePartType.cs
new file mode 100644
index 00000000000..3400f171256
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplatePartType.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ enum UriTemplatePartType
+ {
+ Literal,
+ Compound,
+ Variable
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplatePathPartiallyEquivalentSet.cs b/src/System.ServiceModel.Web/src/System/UriTemplatePathPartiallyEquivalentSet.cs
new file mode 100644
index 00000000000..c18fb4f32d9
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplatePathPartiallyEquivalentSet.cs
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Generic;
+
+ // This class was named UriTemplatePathEquivalentSet in the Orcas bits, where it used to
+ // represent a set of uri templates, which are equivalent in thier path. The introduction
+ // of terminal defaults, caused it to be no longer true; now it is representing a set
+ // of templates, which are equivalent in their path till a certian point, which is stored
+ // in the segmentsCount field. To highlight that fact the class was renamed as
+ // UriTemplatePathPartiallyEquivalentSet.
+ class UriTemplatePathPartiallyEquivalentSet
+ {
+ List> kvps;
+ int segmentsCount;
+
+ public UriTemplatePathPartiallyEquivalentSet(int segmentsCount)
+ {
+ this.segmentsCount = segmentsCount;
+ this.kvps = new List>();
+ }
+ public List> Items
+ {
+ get
+ {
+ return this.kvps;
+ }
+ }
+
+ public int SegmentsCount
+ {
+ get
+ {
+ return this.segmentsCount;
+ }
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplatePathSegment.cs b/src/System.ServiceModel.Web/src/System/UriTemplatePathSegment.cs
new file mode 100644
index 00000000000..ad020115d1c
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplatePathSegment.cs
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Specialized;
+ using System.Diagnostics;
+ using System.Runtime;
+ using System.Text;
+
+ // This represents a Path segment, which can either be a Literal, a Variable or a Compound
+ [DebuggerDisplay("Segment={originalSegment} Nature={nature}")]
+ abstract class UriTemplatePathSegment
+ {
+ readonly bool endsWithSlash;
+ readonly UriTemplatePartType nature;
+ readonly string originalSegment;
+
+ protected UriTemplatePathSegment(string originalSegment, UriTemplatePartType nature,
+ bool endsWithSlash)
+ {
+ this.originalSegment = originalSegment;
+ this.nature = nature;
+ this.endsWithSlash = endsWithSlash;
+ }
+ public bool EndsWithSlash
+ {
+ get
+ {
+ return this.endsWithSlash;
+ }
+ }
+ public UriTemplatePartType Nature
+ {
+ get
+ {
+ return this.nature;
+ }
+ }
+
+ public string OriginalSegment
+ {
+ get
+ {
+ return this.originalSegment;
+ }
+ }
+ public static UriTemplatePathSegment CreateFromUriTemplate(string segment, UriTemplate template)
+ {
+ // Identifying the type of segment - Literal|Compound|Variable
+ switch (UriTemplateHelpers.IdentifyPartType(segment))
+ {
+ case UriTemplatePartType.Literal:
+ return UriTemplateLiteralPathSegment.CreateFromUriTemplate(segment, template);
+
+ case UriTemplatePartType.Compound:
+ return UriTemplateCompoundPathSegment.CreateFromUriTemplate(segment, template);
+
+ case UriTemplatePartType.Variable:
+ if (segment.EndsWith("/", StringComparison.Ordinal))
+ {
+ string varName = template.AddPathVariable(UriTemplatePartType.Variable,
+ segment.Substring(1, segment.Length - 3));
+ return new UriTemplateVariablePathSegment(segment, true, varName);
+ }
+ else
+ {
+ string varName = template.AddPathVariable(UriTemplatePartType.Variable,
+ segment.Substring(1, segment.Length - 2));
+ return new UriTemplateVariablePathSegment(segment, false, varName);
+ }
+
+ default:
+ Fx.Assert("Invalid value from IdentifyStringNature");
+ return null;
+ }
+ }
+ public abstract void Bind(string[] values, ref int valueIndex, StringBuilder path);
+
+ public abstract bool IsEquivalentTo(UriTemplatePathSegment other, bool ignoreTrailingSlash);
+ public bool IsMatch(UriTemplateLiteralPathSegment segment)
+ {
+ return IsMatch(segment, false);
+ }
+ public abstract bool IsMatch(UriTemplateLiteralPathSegment segment, bool ignoreTrailingSlash);
+ public abstract void Lookup(string segment, NameValueCollection boundParameters);
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateQueryValue.cs b/src/System.ServiceModel.Web/src/System/UriTemplateQueryValue.cs
new file mode 100644
index 00000000000..ecc0929fa1b
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateQueryValue.cs
@@ -0,0 +1,104 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Specialized;
+ using System.Runtime;
+ using System.ServiceModel;
+ using System.ServiceModel.Channels;
+ using System.Text;
+
+ // This represents a Query value, which can either be Empty, a Literal or a Variable
+ abstract class UriTemplateQueryValue
+ {
+
+ readonly UriTemplatePartType nature;
+ static UriTemplateQueryValue empty = new EmptyUriTemplateQueryValue();
+
+ protected UriTemplateQueryValue(UriTemplatePartType nature)
+ {
+ this.nature = nature;
+ }
+
+ public static UriTemplateQueryValue Empty
+ {
+ get
+ {
+ return UriTemplateQueryValue.empty;
+ }
+ }
+
+ public UriTemplatePartType Nature
+ {
+ get
+ {
+ return this.nature;
+ }
+ }
+ public static UriTemplateQueryValue CreateFromUriTemplate(string value, UriTemplate template)
+ {
+ // Checking for empty value
+ if (value == null)
+ {
+ return UriTemplateQueryValue.Empty;
+ }
+ // Identifying the type of value - Literal|Compound|Variable
+ switch (UriTemplateHelpers.IdentifyPartType(value))
+ {
+ case UriTemplatePartType.Literal:
+ return UriTemplateLiteralQueryValue.CreateFromUriTemplate(value);
+
+ case UriTemplatePartType.Compound:
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTQueryCannotHaveCompoundValue, template.originalTemplate)));
+
+ case UriTemplatePartType.Variable:
+ return new UriTemplateVariableQueryValue(template.AddQueryVariable(value.Substring(1, value.Length - 2)));
+
+ default:
+ Fx.Assert("Invalid value from IdentifyStringNature");
+ return null;
+ }
+ }
+
+ public static bool IsNullOrEmpty(UriTemplateQueryValue utqv)
+ {
+ if (utqv == null)
+ {
+ return true;
+ }
+ if (utqv == UriTemplateQueryValue.Empty)
+ {
+ return true;
+ }
+ return false;
+ }
+ public abstract void Bind(string keyName, string[] values, ref int valueIndex, StringBuilder query);
+
+ public abstract bool IsEquivalentTo(UriTemplateQueryValue other);
+ public abstract void Lookup(string value, NameValueCollection boundParameters);
+
+ class EmptyUriTemplateQueryValue : UriTemplateQueryValue
+ {
+ public EmptyUriTemplateQueryValue()
+ : base(UriTemplatePartType.Literal)
+ {
+ }
+ public override void Bind(string keyName, string[] values, ref int valueIndex, StringBuilder query)
+ {
+ query.AppendFormat("&{0}", UrlUtility.UrlEncode(keyName, Encoding.UTF8));
+ }
+
+ public override bool IsEquivalentTo(UriTemplateQueryValue other)
+ {
+ return (other == UriTemplateQueryValue.Empty);
+ }
+ public override void Lookup(string value, NameValueCollection boundParameters)
+ {
+ Fx.Assert(string.IsNullOrEmpty(value), "shouldn't have a value");
+ }
+ }
+ }
+
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateTable.cs b/src/System.ServiceModel.Web/src/System/UriTemplateTable.cs
new file mode 100644
index 00000000000..6ac2a9c242d
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateTable.cs
@@ -0,0 +1,607 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Collections.Specialized;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Runtime;
+ using System.ServiceModel;
+ using System.Runtime.CompilerServices;
+
+ // this class is thread-safe
+ [TypeForwardedFrom("System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
+ public class UriTemplateTable
+ {
+ Uri baseAddress;
+ string basePath;
+ Dictionary fastPathTable; // key is uri.PathAndQuery, fastPathTable may be null
+ bool noTemplateHasQueryPart;
+ int numSegmentsInBaseAddress;
+ Uri originalUncanonicalizedBaseAddress;
+ UriTemplateTrieNode rootNode;
+ UriTemplatesCollection templates;
+ object thisLock;
+ bool addTrailingSlashToBaseAddress;
+
+ public UriTemplateTable()
+ : this(null, null, true)
+ {
+ }
+ public UriTemplateTable(IEnumerable> keyValuePairs)
+ : this(null, keyValuePairs, true)
+ {
+ }
+ public UriTemplateTable(Uri baseAddress)
+ : this(baseAddress, null, true)
+ {
+ }
+
+ internal UriTemplateTable(Uri baseAddress, bool addTrailingSlashToBaseAddress)
+ : this(baseAddress, null, addTrailingSlashToBaseAddress)
+ {
+ }
+
+ public UriTemplateTable(Uri baseAddress, IEnumerable> keyValuePairs)
+ : this(baseAddress, keyValuePairs, true)
+ {
+ }
+
+ internal UriTemplateTable(Uri baseAddress, IEnumerable> keyValuePairs, bool addTrailingSlashToBaseAddress)
+ {
+ if (baseAddress != null && !baseAddress.IsAbsoluteUri)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("baseAddress", SR.Format(
+ SR.UTTMustBeAbsolute));
+ }
+
+ this.addTrailingSlashToBaseAddress = addTrailingSlashToBaseAddress;
+ this.originalUncanonicalizedBaseAddress = baseAddress;
+
+ if (keyValuePairs != null)
+ {
+ this.templates = new UriTemplatesCollection(keyValuePairs);
+ }
+ else
+ {
+ this.templates = new UriTemplatesCollection();
+ }
+
+ this.thisLock = new object();
+ this.baseAddress = baseAddress;
+ NormalizeBaseAddress();
+ }
+
+ public Uri BaseAddress
+ {
+ get
+ {
+ return this.baseAddress;
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
+ }
+ lock (this.thisLock)
+ {
+ if (this.IsReadOnly)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
+ SR.Format(SR.UTTCannotChangeBaseAddress)));
+ }
+ else
+ {
+ if (!value.IsAbsoluteUri)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.Format(
+ SR.UTTBaseAddressMustBeAbsolute));
+ }
+ else
+ {
+ this.originalUncanonicalizedBaseAddress = value;
+ this.baseAddress = value;
+ NormalizeBaseAddress();
+ }
+ }
+ }
+ }
+ }
+
+ public Uri OriginalBaseAddress
+ {
+ get
+ {
+ return this.originalUncanonicalizedBaseAddress;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return this.templates.IsFrozen;
+ }
+ }
+ public IList> KeyValuePairs
+ {
+ get
+ {
+ return this.templates;
+ }
+ }
+
+ public void MakeReadOnly(bool allowDuplicateEquivalentUriTemplates)
+ {
+ // idempotent
+ lock (this.thisLock)
+ {
+ if (!this.IsReadOnly)
+ {
+ this.templates.Freeze();
+ Validate(allowDuplicateEquivalentUriTemplates);
+ ConstructFastPathTable();
+ }
+ }
+ }
+ public Collection Match(Uri uri)
+ {
+ if (uri == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("uri");
+ }
+ if (!uri.IsAbsoluteUri)
+ {
+ return None();
+ }
+
+ this.MakeReadOnly(true);
+
+ // Matching path :
+ Collection relativeSegments;
+ IList candidates;
+ if (!FastComputeRelativeSegmentsAndLookup(uri, out relativeSegments, out candidates))
+ {
+ return None();
+ }
+ // Matching query :
+ NameValueCollection queryParameters = null;
+ if (!this.noTemplateHasQueryPart && AtLeastOneCandidateHasQueryPart(candidates))
+ {
+ Collection nextCandidates = new Collection();
+ Fx.Assert(nextCandidates.Count == 0, "nextCandidates should be empty");
+
+ // then deal with query
+ queryParameters = UriTemplateHelpers.ParseQueryString(uri.Query);
+ bool mustBeEspeciallyInteresting = NoCandidateHasQueryLiteralRequirementsAndThereIsAnEmptyFallback(candidates);
+ for (int i = 0; i < candidates.Count; i++)
+ {
+ if (UriTemplateHelpers.CanMatchQueryInterestingly(candidates[i].Template, queryParameters, mustBeEspeciallyInteresting))
+ {
+ nextCandidates.Add(candidates[i]);
+ }
+ }
+ if (nextCandidates.Count > 1)
+ {
+ Fx.Assert(AllEquivalent(nextCandidates, 0, nextCandidates.Count), "demux algorithm problem, multiple non-equivalent matches");
+ }
+
+ if (nextCandidates.Count == 0)
+ {
+ for (int i = 0; i < candidates.Count; i++)
+ {
+ if (UriTemplateHelpers.CanMatchQueryTrivially(candidates[i].Template))
+ {
+ nextCandidates.Add(candidates[i]);
+ }
+ }
+ }
+ if (nextCandidates.Count == 0)
+ {
+ return None();
+ }
+ if (nextCandidates.Count > 1)
+ {
+ Fx.Assert(AllEquivalent(nextCandidates, 0, nextCandidates.Count), "demux algorithm problem, multiple non-equivalent matches");
+ }
+
+ candidates = nextCandidates;
+ }
+ // Verifying that we have not broken the allowDuplicates settings because of terminal defaults
+ // This situation can be caused when we are hosting ".../" and ".../{foo=xyz}" in the same
+ // table. They are not equivalent; yet they reside together in the same path partially-equivalent
+ // set. If we hit a uri that ends up in that particular end-of-path set, we want to provide the
+ // user only the 'best' match and not both; thus preventing inconsistancy between the MakeReadonly
+ // settings and the matching results. We will assume that the 'best' matches will be the ones with
+ // the smallest number of segments - this will prefer ".../" over ".../{x=1}[/...]".
+ if (NotAllCandidatesArePathFullyEquivalent(candidates))
+ {
+ Collection nextCandidates = new Collection();
+ int minSegmentsCount = -1;
+ for (int i = 0; i < candidates.Count; i++)
+ {
+ UriTemplateTableMatchCandidate candidate = candidates[i];
+ if (minSegmentsCount == -1)
+ {
+ minSegmentsCount = candidate.Template.segments.Count;
+ nextCandidates.Add(candidate);
+ }
+ else if (candidate.Template.segments.Count < minSegmentsCount)
+ {
+ minSegmentsCount = candidate.Template.segments.Count;
+ nextCandidates.Clear();
+ nextCandidates.Add(candidate);
+ }
+ else if (candidate.Template.segments.Count == minSegmentsCount)
+ {
+ nextCandidates.Add(candidate);
+ }
+ }
+ Fx.Assert(minSegmentsCount != -1, "At least the first entry in the list should be kept");
+ Fx.Assert(nextCandidates.Count >= 1, "At least the first entry in the list should be kept");
+ Fx.Assert(nextCandidates[0].Template.segments.Count == minSegmentsCount, "Trivial");
+ candidates = nextCandidates;
+ }
+
+ // Building the actual result
+ Collection actualResults = new Collection();
+ for (int i = 0; i < candidates.Count; i++)
+ {
+ UriTemplateTableMatchCandidate candidate = candidates[i];
+ UriTemplateMatch match = candidate.Template.CreateUriTemplateMatch(this.originalUncanonicalizedBaseAddress,
+ uri, candidate.Data, candidate.SegmentsCount, relativeSegments, queryParameters);
+ actualResults.Add(match);
+ }
+ return actualResults;
+ }
+ public UriTemplateMatch MatchSingle(Uri uri)
+ {
+ Collection c = this.Match(uri);
+ if (c.Count == 0)
+ {
+ return null;
+ }
+ if (c.Count == 1)
+ {
+ return c[0];
+ }
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new UriTemplateMatchException(SR.Format(
+ SR.UTTMultipleMatches)));
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is called within a Debug assert")]
+ static bool AllEquivalent(IList list, int a, int b)
+ {
+ for (int i = a; i < b - 1; ++i)
+ {
+ if (!list[i].Template.IsPathPartiallyEquivalentAt(list[i + 1].Template, list[i].SegmentsCount))
+ {
+ return false;
+ }
+ if (!list[i].Template.IsQueryEquivalent(list[i + 1].Template))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static bool AtLeastOneCandidateHasQueryPart(IList candidates)
+ {
+ for (int i = 0; i < candidates.Count; i++)
+ {
+ if (!UriTemplateHelpers.CanMatchQueryTrivially(candidates[i].Template))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ static bool NoCandidateHasQueryLiteralRequirementsAndThereIsAnEmptyFallback(
+ IList candidates)
+ {
+ bool thereIsAmEmptyFallback = false;
+ for (int i = 0; i < candidates.Count; i++)
+ {
+ if (UriTemplateHelpers.HasQueryLiteralRequirements(candidates[i].Template))
+ {
+ return false;
+ }
+ if (candidates[i].Template.queries.Count == 0)
+ {
+ thereIsAmEmptyFallback = true;
+ }
+ }
+ return thereIsAmEmptyFallback;
+ }
+
+ static Collection None()
+ {
+ return new Collection();
+ }
+ static bool NotAllCandidatesArePathFullyEquivalent(IList candidates)
+ {
+ if (candidates.Count <= 1)
+ {
+ return false;
+ }
+
+ int segmentsCount = -1;
+ for (int i = 0; i < candidates.Count; i++)
+ {
+ if (segmentsCount == -1)
+ {
+ segmentsCount = candidates[i].Template.segments.Count;
+ }
+ else if (segmentsCount != candidates[i].Template.segments.Count)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool ComputeRelativeSegmentsAndLookup(Uri uri,
+ ICollection relativePathSegments, // add to this
+ ICollection candidates) // matched candidates
+ {
+ string[] uriSegments = uri.Segments;
+ int numRelativeSegments = uriSegments.Length - this.numSegmentsInBaseAddress;
+ Fx.Assert(numRelativeSegments >= 0, "bad num segments");
+ UriTemplateLiteralPathSegment[] uSegments = new UriTemplateLiteralPathSegment[numRelativeSegments];
+ for (int i = 0; i < numRelativeSegments; ++i)
+ {
+ string seg = uriSegments[i + this.numSegmentsInBaseAddress];
+ // compute representation for matching
+ UriTemplateLiteralPathSegment lps = UriTemplateLiteralPathSegment.CreateFromWireData(seg);
+ uSegments[i] = lps;
+ // compute representation to project out into results
+ string relPathSeg = Uri.UnescapeDataString(seg);
+ if (lps.EndsWithSlash)
+ {
+ Fx.Assert(relPathSeg.EndsWith("/", StringComparison.Ordinal), "problem with relative path segment");
+ relPathSeg = relPathSeg.Substring(0, relPathSeg.Length - 1); // trim slash
+ }
+ relativePathSegments.Add(relPathSeg);
+ }
+ return rootNode.Match(uSegments, candidates);
+ }
+ void ConstructFastPathTable()
+ {
+ this.noTemplateHasQueryPart = true;
+ foreach (KeyValuePair kvp in this.templates)
+ {
+ UriTemplate ut = kvp.Key;
+ if (!UriTemplateHelpers.CanMatchQueryTrivially(ut))
+ {
+ this.noTemplateHasQueryPart = false;
+ }
+ if (ut.HasNoVariables && !ut.HasWildcard)
+ {
+ // eligible for fast path
+ if (this.fastPathTable == null)
+ {
+ this.fastPathTable = new Dictionary();
+ }
+ Uri uri = ut.BindByPosition(this.originalUncanonicalizedBaseAddress);
+ string uriPath = UriTemplateHelpers.GetUriPath(uri);
+ if (this.fastPathTable.ContainsKey(uriPath))
+ {
+ // nothing to do, we've already seen it
+ }
+ else
+ {
+ FastPathInfo fpInfo = new FastPathInfo();
+ if (ComputeRelativeSegmentsAndLookup(uri, fpInfo.RelativePathSegments,
+ fpInfo.Candidates))
+ {
+ fpInfo.Freeze();
+ this.fastPathTable.Add(uriPath, fpInfo);
+ }
+ }
+ }
+ }
+ }
+ // this method checks the literal cache for a match if none, goes through the slower path of cracking the segments
+ bool FastComputeRelativeSegmentsAndLookup(Uri uri, out Collection relativePathSegments,
+ out IList candidates)
+ {
+ // Consider fast-path and lookup
+ // return false if not under base uri
+ string uriPath = UriTemplateHelpers.GetUriPath(uri);
+ FastPathInfo fpInfo = null;
+ if ((this.fastPathTable != null) && this.fastPathTable.TryGetValue(uriPath, out fpInfo))
+ {
+ relativePathSegments = fpInfo.RelativePathSegments;
+ candidates = fpInfo.Candidates;
+ VerifyThatFastPathAndSlowPathHaveSameResults(uri, relativePathSegments, candidates);
+ return true;
+ }
+ else
+ {
+ relativePathSegments = new Collection();
+ candidates = new Collection();
+ return SlowComputeRelativeSegmentsAndLookup(uri, uriPath, relativePathSegments, candidates);
+ }
+ }
+
+ void NormalizeBaseAddress()
+ {
+ if (this.baseAddress != null)
+ {
+ // ensure trailing slash on baseAddress, so that IsBaseOf will work later
+ UriBuilder ub = new UriBuilder(this.baseAddress);
+ if (this.addTrailingSlashToBaseAddress && !ub.Path.EndsWith("/", StringComparison.Ordinal))
+ {
+ ub.Path = ub.Path + "/";
+ }
+ ub.Host = "localhost"; // always normalize to localhost
+ ub.Port = -1;
+ ub.UserName = null;
+ ub.Password = null;
+ ub.Path = ub.Path.ToUpperInvariant();
+ ub.Scheme = Uri.UriSchemeHttp;
+ this.baseAddress = ub.Uri;
+ basePath = UriTemplateHelpers.GetUriPath(this.baseAddress);
+ }
+ }
+ bool SlowComputeRelativeSegmentsAndLookup(Uri uri, string uriPath, Collection relativePathSegments,
+ ICollection candidates)
+ {
+ // ensure 'under' the base address
+ if (uriPath.Length < basePath.Length)
+ {
+ return false;
+ }
+
+ if (!uriPath.StartsWith(basePath, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ else
+ {
+ // uriPath StartsWith basePath, but this check is not enough - basePath 'service1' should not match with uriPath 'service123'
+ // make sure that after the match the next character is /, this is to avoid a uriPath of the form /service12/ matching with a basepath of the form /service1
+ if (uriPath.Length > basePath.Length && !basePath.EndsWith("/", StringComparison.Ordinal) && uriPath[basePath.Length] != '/')
+ {
+ return false;
+ }
+ }
+
+ return ComputeRelativeSegmentsAndLookup(uri, relativePathSegments, candidates);
+ }
+
+ void Validate(bool allowDuplicateEquivalentUriTemplates)
+ {
+ if (this.baseAddress == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTTBaseAddressNotSet)));
+ }
+ this.numSegmentsInBaseAddress = this.baseAddress.Segments.Length;
+ if (this.templates.Count == 0)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTTEmptyKeyValuePairs)));
+ }
+ // build the trie and
+ // validate that forall Uri u, at most one UriTemplate is a best match for u
+ rootNode = UriTemplateTrieNode.Make(this.templates, allowDuplicateEquivalentUriTemplates);
+ }
+ [Conditional("DEBUG")]
+ void VerifyThatFastPathAndSlowPathHaveSameResults(Uri uri, Collection fastPathRelativePathSegments,
+ IList fastPathCandidates)
+ {
+ Collection slowPathRelativePathSegments = new Collection();
+ List slowPathCandidates = new List();
+ if (!SlowComputeRelativeSegmentsAndLookup(uri, UriTemplateHelpers.GetUriPath(uri),
+ slowPathRelativePathSegments, slowPathCandidates))
+ {
+ Fx.Assert("fast path yielded a result but slow path yielded no result");
+ }
+ // compare results
+ if (fastPathRelativePathSegments.Count != slowPathRelativePathSegments.Count)
+ {
+ Fx.Assert("fast path yielded different number of segments from slow path");
+ }
+ for (int i = 0; i < fastPathRelativePathSegments.Count; ++i)
+ {
+ if (fastPathRelativePathSegments[i] != slowPathRelativePathSegments[i])
+ {
+ Fx.Assert("fast path yielded different segments from slow path");
+ }
+ }
+ if (fastPathCandidates.Count != slowPathCandidates.Count)
+ {
+ Fx.Assert("fast path yielded different number of candidates from slow path");
+ }
+ for (int i = 0; i < fastPathCandidates.Count; i++)
+ {
+ if (!slowPathCandidates.Contains(fastPathCandidates[i]))
+ {
+ Fx.Assert("fast path yielded different candidates from slow path");
+ }
+ }
+ }
+
+ class FastPathInfo
+ {
+ FreezableCollection candidates;
+ FreezableCollection relativePathSegments;
+
+ public FastPathInfo()
+ {
+ this.relativePathSegments = new FreezableCollection();
+ this.candidates = new FreezableCollection();
+ }
+ public Collection Candidates
+ {
+ get
+ {
+ return this.candidates;
+ }
+ }
+
+ public Collection RelativePathSegments
+ {
+ get
+ {
+ return this.relativePathSegments;
+ }
+ }
+
+ public void Freeze()
+ {
+ this.relativePathSegments.Freeze();
+ this.candidates.Freeze();
+ }
+ }
+
+ class UriTemplatesCollection : FreezableCollection>
+ {
+ public UriTemplatesCollection()
+ : base()
+ {
+ }
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "This is a private class; virtual methods cannot be overriden")]
+ public UriTemplatesCollection(IEnumerable> keyValuePairs)
+ : base()
+ {
+ foreach (KeyValuePair kvp in keyValuePairs)
+ {
+ ThrowIfInvalid(kvp.Key, "keyValuePairs");
+ base.Add(kvp);
+ }
+ }
+
+ protected override void InsertItem(int index, KeyValuePair item)
+ {
+ ThrowIfInvalid(item.Key, "item");
+ base.InsertItem(index, item);
+ }
+ protected override void SetItem(int index, KeyValuePair item)
+ {
+ ThrowIfInvalid(item.Key, "item");
+ base.SetItem(index, item);
+ }
+
+ static void ThrowIfInvalid(UriTemplate template, string argName)
+ {
+ if (template == null)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(argName,
+ SR.Format(SR.UTTNullTemplateKey));
+ }
+ if (template.IgnoreTrailingSlash)
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(argName,
+ SR.Format(SR.UTTInvalidTemplateKey, template));
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateTableMatchCandidate.cs b/src/System.ServiceModel.Web/src/System/UriTemplateTableMatchCandidate.cs
new file mode 100644
index 00000000000..6a73be1d0e1
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateTableMatchCandidate.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ struct UriTemplateTableMatchCandidate
+ {
+ readonly object data;
+ readonly int segmentsCount;
+ readonly UriTemplate template;
+
+ public UriTemplateTableMatchCandidate(UriTemplate template, int segmentsCount, object data)
+ {
+ this.template = template;
+ this.segmentsCount = segmentsCount;
+ this.data = data;
+ }
+ public object Data
+ {
+ get
+ {
+ return this.data;
+ }
+ }
+ public int SegmentsCount
+ {
+ get
+ {
+ return this.segmentsCount;
+ }
+ }
+
+ public UriTemplate Template
+ {
+ get
+ {
+ return this.template;
+ }
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateTrieIntraNodeLocation.cs b/src/System.ServiceModel.Web/src/System/UriTemplateTrieIntraNodeLocation.cs
new file mode 100644
index 00000000000..a40b1e987da
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateTrieIntraNodeLocation.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ enum UriTemplateTrieIntraNodeLocation
+ {
+ BeforeLiteral,
+ AfterLiteral,
+ AfterCompound,
+ AfterVariable,
+ }
+
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateTrieLocation.cs b/src/System.ServiceModel.Web/src/System/UriTemplateTrieLocation.cs
new file mode 100644
index 00000000000..b76c979361e
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateTrieLocation.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ class UriTemplateTrieLocation
+ {
+ public UriTemplateTrieIntraNodeLocation locationWithin;
+ public UriTemplateTrieNode node;
+ public UriTemplateTrieLocation(UriTemplateTrieNode n, UriTemplateTrieIntraNodeLocation i)
+ {
+ this.node = n;
+ this.locationWithin = i;
+ }
+ }
+
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateTrieNode.cs b/src/System.ServiceModel.Web/src/System/UriTemplateTrieNode.cs
new file mode 100644
index 00000000000..d29034917a3
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateTrieNode.cs
@@ -0,0 +1,805 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Runtime;
+ using System.ServiceModel;
+
+ class UriTemplateTrieNode
+ {
+ int depth; // relative segment depth (root = 0)
+ UriTemplatePathPartiallyEquivalentSet endOfPath; // matches the non-existent segment at the end of a slash-terminated path
+ AscendingSortedCompoundSegmentsCollection finalCompoundSegment; // matches e.g. "{var}.{var}"
+ Dictionary finalLiteralSegment; // matches e.g. "segmentThatDoesntEndInSlash"
+ UriTemplatePathPartiallyEquivalentSet finalVariableSegment; // matches e.g. "{var}"
+ AscendingSortedCompoundSegmentsCollection nextCompoundSegment; // all are AfterLiteral; matches e.g. "{var}.{var}/"
+ Dictionary nextLiteralSegment; // all are BeforeLiteral; matches e.g. "path/"
+ UriTemplateTrieLocation nextVariableSegment; // is BeforeLiteral; matches e.g. "{var}/"
+ UriTemplateTrieLocation onFailure; // points to parent, at 'after me'
+ UriTemplatePathPartiallyEquivalentSet star; // matches any "extra/path/segments" at the end
+
+ UriTemplateTrieNode(int depth)
+ {
+ this.depth = depth;
+ this.nextLiteralSegment = null;
+ this.nextCompoundSegment = null;
+ this.finalLiteralSegment = null;
+ this.finalCompoundSegment = null;
+ this.finalVariableSegment = new UriTemplatePathPartiallyEquivalentSet(depth + 1);
+ this.star = new UriTemplatePathPartiallyEquivalentSet(depth);
+ this.endOfPath = new UriTemplatePathPartiallyEquivalentSet(depth);
+ }
+
+ public static UriTemplateTrieNode Make(IEnumerable> keyValuePairs,
+ bool allowDuplicateEquivalentUriTemplates)
+ {
+ // given a UTT at MakeReadOnly time, build the trie
+ // note that root.onFailure == null;
+ UriTemplateTrieNode root = new UriTemplateTrieNode(0);
+ foreach (KeyValuePair kvp in keyValuePairs)
+ {
+ Add(root, kvp);
+ }
+ Validate(root, allowDuplicateEquivalentUriTemplates);
+ return root;
+ }
+
+ public bool Match(UriTemplateLiteralPathSegment[] wireData, ICollection candidates)
+ {
+ UriTemplateTrieLocation currentLocation = new UriTemplateTrieLocation(this, UriTemplateTrieIntraNodeLocation.BeforeLiteral);
+ return GetMatch(currentLocation, wireData, candidates);
+ }
+
+ static void Add(UriTemplateTrieNode root, KeyValuePair kvp)
+ {
+ // Currently UTT doesn't support teplates with ignoreTrailingSlash == true; thus we
+ // don't care about supporting it in the trie as well.
+ UriTemplateTrieNode current = root;
+ UriTemplate ut = kvp.Key;
+ bool needProcessingOnFinalNode = ((ut.segments.Count == 0) || ut.HasWildcard ||
+ ut.segments[ut.segments.Count - 1].EndsWithSlash);
+ for (int i = 0; i < ut.segments.Count; ++i)
+ {
+ if (i >= ut.firstOptionalSegment)
+ {
+ current.endOfPath.Items.Add(kvp);
+ }
+ UriTemplatePathSegment ps = ut.segments[i];
+ if (!ps.EndsWithSlash)
+ {
+ Fx.Assert(i == ut.segments.Count - 1, "only the last segment can !EndsWithSlash");
+ Fx.Assert(!ut.HasWildcard, "path star cannot have !EndsWithSlash");
+ switch (ps.Nature)
+ {
+ case UriTemplatePartType.Literal:
+ current.AddFinalLiteralSegment(ps as UriTemplateLiteralPathSegment, kvp);
+ break;
+
+ case UriTemplatePartType.Compound:
+ current.AddFinalCompoundSegment(ps as UriTemplateCompoundPathSegment, kvp);
+ break;
+
+ case UriTemplatePartType.Variable:
+ current.finalVariableSegment.Items.Add(kvp);
+ break;
+
+ default:
+ Fx.Assert("Invalid value as PathSegment.Nature");
+ break;
+ }
+ }
+ else
+ {
+ Fx.Assert(ps.EndsWithSlash, "ps.EndsWithSlash");
+ switch (ps.Nature)
+ {
+ case UriTemplatePartType.Literal:
+ current = current.AddNextLiteralSegment(ps as UriTemplateLiteralPathSegment);
+ break;
+
+ case UriTemplatePartType.Compound:
+ current = current.AddNextCompoundSegment(ps as UriTemplateCompoundPathSegment);
+ break;
+
+ case UriTemplatePartType.Variable:
+ current = current.AddNextVariableSegment();
+ break;
+
+ default:
+ Fx.Assert("Invalid value as PathSegment.Nature");
+ break;
+ }
+ }
+ }
+ if (needProcessingOnFinalNode)
+ {
+ // if the last segment ended in a slash, there is still more to do
+ if (ut.HasWildcard)
+ {
+ // e.g. "path1/path2/*"
+ current.star.Items.Add(kvp);
+ }
+ else
+ {
+ // e.g. "path1/path2/"
+ current.endOfPath.Items.Add(kvp);
+ }
+ }
+ }
+
+ static bool CheckMultipleMatches(IList> locationsSet, UriTemplateLiteralPathSegment[] wireData,
+ ICollection candidates)
+ {
+ bool result = false;
+ for (int i = 0; ((i < locationsSet.Count) && !result); i++)
+ {
+ for (int j = 0; j < locationsSet[i].Count; j++)
+ {
+ if (GetMatch(locationsSet[i][j], wireData, candidates))
+ {
+ result = true;
+ }
+ }
+ }
+ return result;
+ }
+ static bool GetMatch(UriTemplateTrieLocation location, UriTemplateLiteralPathSegment[] wireData,
+ ICollection candidates)
+ {
+ int initialDepth = location.node.depth;
+ SingleLocationOrLocationsSet nextStep;
+ UriTemplatePathPartiallyEquivalentSet answer;
+ do
+ {
+ if (TryMatch(wireData, location, out answer, out nextStep))
+ {
+ if (answer != null)
+ {
+ for (int i = 0; i < answer.Items.Count; i++)
+ {
+ candidates.Add(new UriTemplateTableMatchCandidate(answer.Items[i].Key, answer.SegmentsCount,
+ answer.Items[i].Value));
+ }
+ }
+ return true;
+ }
+ if (nextStep.IsSingle)
+ {
+ location = nextStep.SingleLocation;
+ }
+ else
+ {
+ Fx.Assert(nextStep.LocationsSet != null, "This should be set to a valid value by TryMatch");
+ if (CheckMultipleMatches(nextStep.LocationsSet, wireData, candidates))
+ {
+ return true;
+ }
+ location = GetFailureLocationFromLocationsSet(nextStep.LocationsSet);
+ }
+ } while ((location != null) && (location.node.depth >= initialDepth));
+
+ // we walked the whole trie down and found nothing
+ return false;
+ }
+ static bool TryMatch(UriTemplateLiteralPathSegment[] wireUriSegments, UriTemplateTrieLocation currentLocation,
+ out UriTemplatePathPartiallyEquivalentSet success, out SingleLocationOrLocationsSet nextStep)
+ {
+ // if returns true, success is set to answer
+ // if returns false, nextStep is set to next place to look
+ success = null;
+ nextStep = new SingleLocationOrLocationsSet();
+
+ if (wireUriSegments.Length <= currentLocation.node.depth)
+ {
+ Fx.Assert(wireUriSegments.Length == 0 || wireUriSegments[wireUriSegments.Length - 1].EndsWithSlash,
+ "we should not have traversed this deep into the trie unless the wire path ended in a slash");
+
+ if (currentLocation.node.endOfPath.Items.Count != 0)
+ {
+ // exact match of e.g. "path1/path2/"
+ success = currentLocation.node.endOfPath;
+ return true;
+ }
+ else if (currentLocation.node.star.Items.Count != 0)
+ {
+ // inexact match of e.g. WIRE("path1/path2/") against TEMPLATE("path1/path2/*")
+ success = currentLocation.node.star;
+ return true;
+ }
+ else
+ {
+ nextStep = new SingleLocationOrLocationsSet(currentLocation.node.onFailure);
+ return false;
+ }
+ }
+ else
+ {
+ UriTemplateLiteralPathSegment curWireSeg = wireUriSegments[currentLocation.node.depth];
+ bool considerLiteral = false;
+ bool considerCompound = false;
+ bool considerVariable = false;
+ bool considerStar = false;
+ switch (currentLocation.locationWithin)
+ {
+ case UriTemplateTrieIntraNodeLocation.BeforeLiteral:
+ considerLiteral = true;
+ considerCompound = true;
+ considerVariable = true;
+ considerStar = true;
+ break;
+ case UriTemplateTrieIntraNodeLocation.AfterLiteral:
+ considerLiteral = false;
+ considerCompound = true;
+ considerVariable = true;
+ considerStar = true;
+ break;
+ case UriTemplateTrieIntraNodeLocation.AfterCompound:
+ considerLiteral = false;
+ considerCompound = false;
+ considerVariable = true;
+ considerStar = true;
+ break;
+ case UriTemplateTrieIntraNodeLocation.AfterVariable:
+ considerLiteral = false;
+ considerCompound = false;
+ considerVariable = false;
+ considerStar = true;
+ break;
+ default:
+ Fx.Assert("bad kind");
+ break;
+ }
+ if (curWireSeg.EndsWithSlash)
+ {
+ IList> compoundLocationsSet;
+
+ if (considerLiteral && currentLocation.node.nextLiteralSegment != null &&
+ currentLocation.node.nextLiteralSegment.ContainsKey(curWireSeg))
+ {
+ nextStep = new SingleLocationOrLocationsSet(currentLocation.node.nextLiteralSegment[curWireSeg]);
+ return false;
+ }
+ else if (considerCompound && currentLocation.node.nextCompoundSegment != null &&
+ AscendingSortedCompoundSegmentsCollection.Lookup(currentLocation.node.nextCompoundSegment, curWireSeg, out compoundLocationsSet))
+ {
+ nextStep = new SingleLocationOrLocationsSet(compoundLocationsSet);
+ return false;
+ }
+ else if (considerVariable && currentLocation.node.nextVariableSegment != null &&
+ !curWireSeg.IsNullOrEmpty())
+ {
+ nextStep = new SingleLocationOrLocationsSet(currentLocation.node.nextVariableSegment);
+ return false;
+ }
+ else if (considerStar && currentLocation.node.star.Items.Count != 0)
+ {
+ // matches e.g. WIRE("path1/path2/path3") and TEMPLATE("path1/*")
+ success = currentLocation.node.star;
+ return true;
+ }
+ else
+ {
+ nextStep = new SingleLocationOrLocationsSet(currentLocation.node.onFailure);
+ return false;
+ }
+ }
+ else
+ {
+ IList> compoundPathEquivalentSets;
+
+ Fx.Assert(!curWireSeg.EndsWithSlash, "!curWireSeg.EndsWithSlash");
+ Fx.Assert(!curWireSeg.IsNullOrEmpty(), "!curWireSeg.IsNullOrEmpty()");
+ if (considerLiteral && currentLocation.node.finalLiteralSegment != null &&
+ currentLocation.node.finalLiteralSegment.ContainsKey(curWireSeg))
+ {
+ // matches e.g. WIRE("path1/path2") and TEMPLATE("path1/path2")
+ success = currentLocation.node.finalLiteralSegment[curWireSeg];
+ return true;
+ }
+ else if (considerCompound && currentLocation.node.finalCompoundSegment != null &&
+ AscendingSortedCompoundSegmentsCollection.Lookup(currentLocation.node.finalCompoundSegment, curWireSeg, out compoundPathEquivalentSets))
+ {
+ // matches e.g. WIRE("path1/path2") and TEMPLATE("path1/p{var}th2")
+ // we should take only the highest order match!
+ Fx.Assert(compoundPathEquivalentSets.Count >= 1, "Lookup is expected to return false otherwise");
+ Fx.Assert(compoundPathEquivalentSets[0].Count > 0, "Find shouldn't return empty sublists");
+ if (compoundPathEquivalentSets[0].Count == 1)
+ {
+ success = compoundPathEquivalentSets[0][0];
+ }
+ else
+ {
+ success = new UriTemplatePathPartiallyEquivalentSet(currentLocation.node.depth + 1);
+ for (int i = 0; i < compoundPathEquivalentSets[0].Count; i++)
+ {
+ success.Items.AddRange(compoundPathEquivalentSets[0][i].Items);
+ }
+ }
+ return true;
+ }
+ else if (considerVariable && currentLocation.node.finalVariableSegment.Items.Count != 0)
+ {
+ // matches e.g. WIRE("path1/path2") and TEMPLATE("path1/{var}")
+ success = currentLocation.node.finalVariableSegment;
+ return true;
+ }
+ else if (considerStar && currentLocation.node.star.Items.Count != 0)
+ {
+ // matches e.g. WIRE("path1/path2") and TEMPLATE("path1/*")
+ success = currentLocation.node.star;
+ return true;
+ }
+ else
+ {
+ nextStep = new SingleLocationOrLocationsSet(currentLocation.node.onFailure);
+ return false;
+ }
+ }
+ }
+ }
+
+ static UriTemplateTrieLocation GetFailureLocationFromLocationsSet(IList> locationsSet)
+ {
+ Fx.Assert(locationsSet != null, "Shouldn't be called on null set");
+ Fx.Assert(locationsSet.Count > 0, "Shouldn't be called on empty set");
+ Fx.Assert(locationsSet[0] != null, "Shouldn't be called on a set with null sub-lists");
+ Fx.Assert(locationsSet[0].Count > 0, "Shouldn't be called on a set with empty sub-lists");
+
+ return locationsSet[0][0].node.onFailure;
+ }
+
+ static void Validate(UriTemplateTrieNode root, bool allowDuplicateEquivalentUriTemplates)
+ {
+ // walk the entire tree, and ensure that each PathEquivalentSet is ok (no ambiguous queries),
+ // verify thst compound segments didn't add potentialy multiple matchs;
+ // also Assert various data-structure invariants
+ Queue nodesQueue = new Queue();
+
+ UriTemplateTrieNode current = root;
+ while (true)
+ {
+ // validate all the PathEquivalentSets that live in this node
+ Validate(current.endOfPath, allowDuplicateEquivalentUriTemplates);
+ Validate(current.finalVariableSegment, allowDuplicateEquivalentUriTemplates);
+ Validate(current.star, allowDuplicateEquivalentUriTemplates);
+ if (current.finalLiteralSegment != null)
+ {
+ foreach (KeyValuePair kvp in current.finalLiteralSegment)
+ {
+ Validate(kvp.Value, allowDuplicateEquivalentUriTemplates);
+ }
+ }
+ if (current.finalCompoundSegment != null)
+ {
+ IList> pesLists = current.finalCompoundSegment.Values;
+ for (int i = 0; i < pesLists.Count; i++)
+ {
+ if (!allowDuplicateEquivalentUriTemplates && (pesLists[i].Count > 1))
+ {
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTTDuplicate, pesLists[i][0].Items[0].Key.ToString(), pesLists[i][1].Items[0].Key.ToString())));
+ }
+ for (int j = 0; j < pesLists[i].Count; j++)
+ {
+ Validate(pesLists[i][j], allowDuplicateEquivalentUriTemplates);
+ }
+ }
+ }
+ // deal with children of this node
+ if (current.nextLiteralSegment != null)
+ {
+ foreach (KeyValuePair kvp in current.nextLiteralSegment)
+ {
+ Fx.Assert(kvp.Value.locationWithin == UriTemplateTrieIntraNodeLocation.BeforeLiteral, "forward-pointers should always point to a BeforeLiteral location");
+ Fx.Assert(kvp.Value.node.depth == current.depth + 1, "kvp.Value.node.depth == current.depth + 1");
+ Fx.Assert(kvp.Value.node.onFailure.node == current, "back pointer should point back to here");
+ Fx.Assert(kvp.Value.node.onFailure.locationWithin == UriTemplateTrieIntraNodeLocation.AfterLiteral, "back-pointer should be AfterLiteral");
+ nodesQueue.Enqueue(kvp.Value.node);
+ }
+ }
+ if (current.nextCompoundSegment != null)
+ {
+ IList> locations = current.nextCompoundSegment.Values;
+ for (int i = 0; i < locations.Count; i++)
+ {
+ if (!allowDuplicateEquivalentUriTemplates && (locations[i].Count > 1))
+ {
+ // In the future we might ease up the restrictions and verify if there is realy
+ // a potential multiple match here; for now we are throwing.
+ UriTemplate firstTemplate = FindAnyUriTemplate(locations[i][0].node);
+ UriTemplate secondTemplate = FindAnyUriTemplate(locations[i][1].node);
+ throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
+ SR.UTTDuplicate, firstTemplate.ToString(), secondTemplate.ToString())));
+ }
+ for (int j = 0; j < locations[i].Count; j++)
+ {
+ UriTemplateTrieLocation location = locations[i][j];
+ Fx.Assert(location.locationWithin == UriTemplateTrieIntraNodeLocation.BeforeLiteral, "forward-pointers should always point to a BeforeLiteral location");
+ Fx.Assert(location.node.depth == current.depth + 1, "kvp.Value.node.depth == current.depth + 1");
+ Fx.Assert(location.node.onFailure.node == current, "back pointer should point back to here");
+ Fx.Assert(location.node.onFailure.locationWithin == UriTemplateTrieIntraNodeLocation.AfterCompound, "back-pointer should be AfterCompound");
+ nodesQueue.Enqueue(location.node);
+ }
+ }
+ }
+ if (current.nextVariableSegment != null)
+ {
+ Fx.Assert(current.nextVariableSegment.locationWithin == UriTemplateTrieIntraNodeLocation.BeforeLiteral, "forward-pointers should always point to a BeforeLiteral location");
+ Fx.Assert(current.nextVariableSegment.node.depth == current.depth + 1, "current.nextVariableSegment.node.depth == current.depth + 1");
+ Fx.Assert(current.nextVariableSegment.node.onFailure.node == current, "back pointer should point back to here");
+ Fx.Assert(current.nextVariableSegment.node.onFailure.locationWithin == UriTemplateTrieIntraNodeLocation.AfterVariable, "back-pointer should be AfterVariable");
+ nodesQueue.Enqueue(current.nextVariableSegment.node);
+ }
+ // move on to next bit of work
+ if (nodesQueue.Count == 0)
+ {
+ break;
+ }
+ current = nodesQueue.Dequeue();
+ }
+ }
+ static void Validate(UriTemplatePathPartiallyEquivalentSet pes, bool allowDuplicateEquivalentUriTemplates)
+ {
+ // A set with 0 or 1 items is valid by definition
+ if (pes.Items.Count < 2)
+ {
+ return;
+ }
+ // Assert all paths are partially-equivalent
+ for (int i = 0; i < pes.Items.Count - 1; ++i)
+ {
+ Fx.Assert(pes.Items[i].Key.IsPathPartiallyEquivalentAt(pes.Items[i + 1].Key, pes.SegmentsCount),
+ "all elements of a PES must be path partially-equivalent");
+ }
+ // We will check that the queries disambiguate only for templates, which are
+ // matched completely at the segments count; templates, which are match at
+ // that point due to terminal defaults, will be ruled out.
+ UriTemplate[] a = new UriTemplate[pes.Items.Count];
+ int arrayIndex = 0;
+ foreach (KeyValuePair kvp in pes.Items)
+ {
+ if (pes.SegmentsCount < kvp.Key.segments.Count)
+ {
+ continue;
+ }
+ Fx.Assert(arrayIndex < a.Length, "We made enough room for all the items");
+ a[arrayIndex++] = kvp.Key;
+ }
+ // Ensure that queries disambiguate (if needed) :
+ if (arrayIndex > 0)
+ {
+ UriTemplateHelpers.DisambiguateSamePath(a, 0, arrayIndex, allowDuplicateEquivalentUriTemplates);
+ }
+ }
+
+ static UriTemplate FindAnyUriTemplate(UriTemplateTrieNode node)
+ {
+ while (node != null)
+ {
+ if (node.endOfPath.Items.Count > 0)
+ {
+ return node.endOfPath.Items[0].Key;
+ }
+ if (node.finalVariableSegment.Items.Count > 0)
+ {
+ return node.finalVariableSegment.Items[0].Key;
+ }
+ if (node.star.Items.Count > 0)
+ {
+ return node.star.Items[0].Key;
+ }
+ if (node.finalLiteralSegment != null)
+ {
+ UriTemplatePathPartiallyEquivalentSet pes =
+ GetAnyDictionaryValue(node.finalLiteralSegment);
+ Fx.Assert(pes.Items.Count > 0, "Otherwise, why creating the dictionary?");
+ return pes.Items[0].Key;
+ }
+ if (node.finalCompoundSegment != null)
+ {
+ UriTemplatePathPartiallyEquivalentSet pes = node.finalCompoundSegment.GetAnyValue();
+ Fx.Assert(pes.Items.Count > 0, "Otherwise, why creating the collection?");
+ return pes.Items[0].Key;
+ }
+
+ if (node.nextLiteralSegment != null)
+ {
+ UriTemplateTrieLocation location =
+ GetAnyDictionaryValue(node.nextLiteralSegment);
+ node = location.node;
+ }
+ else if (node.nextCompoundSegment != null)
+ {
+ UriTemplateTrieLocation location = node.nextCompoundSegment.GetAnyValue();
+ node = location.node;
+ }
+ else if (node.nextVariableSegment != null)
+ {
+ node = node.nextVariableSegment.node;
+ }
+ else
+ {
+ node = null;
+ }
+ }
+ Fx.Assert("How did we got here without finding a UriTemplate earlier?");
+ return null;
+ }
+ static T GetAnyDictionaryValue(IDictionary dictionary)
+ {
+ using (IEnumerator valuesEnumerator = dictionary.Values.GetEnumerator())
+ {
+ valuesEnumerator.MoveNext();
+ return valuesEnumerator.Current;
+ }
+ }
+
+ void AddFinalCompoundSegment(UriTemplateCompoundPathSegment cps, KeyValuePair kvp)
+ {
+ Fx.Assert(cps != null, "must be - based on the segment nature");
+ if (this.finalCompoundSegment == null)
+ {
+ this.finalCompoundSegment = new AscendingSortedCompoundSegmentsCollection();
+ }
+ UriTemplatePathPartiallyEquivalentSet pes = this.finalCompoundSegment.Find(cps);
+ if (pes == null)
+ {
+ pes = new UriTemplatePathPartiallyEquivalentSet(this.depth + 1);
+ this.finalCompoundSegment.Add(cps, pes);
+ }
+ pes.Items.Add(kvp);
+ }
+ void AddFinalLiteralSegment(UriTemplateLiteralPathSegment lps, KeyValuePair kvp)
+ {
+ Fx.Assert(lps != null, "must be - based on the segment nature");
+ if (this.finalLiteralSegment != null && this.finalLiteralSegment.ContainsKey(lps))
+ {
+ this.finalLiteralSegment[lps].Items.Add(kvp);
+ }
+ else
+ {
+ if (this.finalLiteralSegment == null)
+ {
+ this.finalLiteralSegment = new Dictionary();
+ }
+ UriTemplatePathPartiallyEquivalentSet pes = new UriTemplatePathPartiallyEquivalentSet(this.depth + 1);
+ pes.Items.Add(kvp);
+ this.finalLiteralSegment.Add(lps, pes);
+ }
+ }
+ UriTemplateTrieNode AddNextCompoundSegment(UriTemplateCompoundPathSegment cps)
+ {
+ Fx.Assert(cps != null, "must be - based on the segment nature");
+ if (this.nextCompoundSegment == null)
+ {
+ this.nextCompoundSegment = new AscendingSortedCompoundSegmentsCollection();
+ }
+ UriTemplateTrieLocation nextLocation = this.nextCompoundSegment.Find(cps);
+ if (nextLocation == null)
+ {
+ UriTemplateTrieNode nextNode = new UriTemplateTrieNode(this.depth + 1);
+ nextNode.onFailure = new UriTemplateTrieLocation(this, UriTemplateTrieIntraNodeLocation.AfterCompound);
+ nextLocation = new UriTemplateTrieLocation(nextNode, UriTemplateTrieIntraNodeLocation.BeforeLiteral);
+ this.nextCompoundSegment.Add(cps, nextLocation);
+ }
+ return nextLocation.node;
+ }
+ UriTemplateTrieNode AddNextLiteralSegment(UriTemplateLiteralPathSegment lps)
+ {
+ Fx.Assert(lps != null, "must be - based on the segment nature");
+ if (this.nextLiteralSegment != null && this.nextLiteralSegment.ContainsKey(lps))
+ {
+ return this.nextLiteralSegment[lps].node;
+ }
+ else
+ {
+ if (this.nextLiteralSegment == null)
+ {
+ this.nextLiteralSegment = new Dictionary();
+ }
+ UriTemplateTrieNode newNode = new UriTemplateTrieNode(this.depth + 1);
+ newNode.onFailure = new UriTemplateTrieLocation(this, UriTemplateTrieIntraNodeLocation.AfterLiteral);
+ this.nextLiteralSegment.Add(lps, new UriTemplateTrieLocation(newNode, UriTemplateTrieIntraNodeLocation.BeforeLiteral));
+ return newNode;
+ }
+ }
+ UriTemplateTrieNode AddNextVariableSegment()
+ {
+ if (this.nextVariableSegment != null)
+ {
+ return this.nextVariableSegment.node;
+ }
+ else
+ {
+ UriTemplateTrieNode newNode = new UriTemplateTrieNode(this.depth + 1);
+ newNode.onFailure = new UriTemplateTrieLocation(this, UriTemplateTrieIntraNodeLocation.AfterVariable);
+ this.nextVariableSegment = new UriTemplateTrieLocation(newNode, UriTemplateTrieIntraNodeLocation.BeforeLiteral);
+ return newNode;
+ }
+ }
+
+ struct SingleLocationOrLocationsSet
+ {
+ readonly bool isSingle;
+ readonly IList> locationsSet;
+ readonly UriTemplateTrieLocation singleLocation;
+
+ public SingleLocationOrLocationsSet(UriTemplateTrieLocation singleLocation)
+ {
+ this.isSingle = true;
+ this.singleLocation = singleLocation;
+ this.locationsSet = null;
+ }
+ public SingleLocationOrLocationsSet(IList> locationsSet)
+ {
+ this.isSingle = false;
+ this.singleLocation = null;
+ this.locationsSet = locationsSet;
+ }
+
+ public bool IsSingle
+ {
+ get
+ {
+ return this.isSingle;
+ }
+ }
+ public IList> LocationsSet
+ {
+ get
+ {
+ Fx.Assert(!this.isSingle, "!this.isSingle");
+ return this.locationsSet;
+ }
+ }
+ public UriTemplateTrieLocation SingleLocation
+ {
+ get
+ {
+ Fx.Assert(this.isSingle, "this.isSingle");
+ return this.singleLocation;
+ }
+ }
+ }
+
+ class AscendingSortedCompoundSegmentsCollection
+ where T : class
+ {
+ SortedList> items;
+
+ public AscendingSortedCompoundSegmentsCollection()
+ {
+ this.items = new SortedList.CollectionItem>>();
+ }
+
+ public IList> Values
+ {
+ get
+ {
+ IList> results = new List>(this.items.Count);
+ for (int i = 0; i < this.items.Values.Count; i++)
+ {
+ results.Add(new List(this.items.Values[i].Count));
+ Fx.Assert(results.Count == i + 1, "We are adding item for each values collection");
+ for (int j = 0; j < this.items.Values[i].Count; j++)
+ {
+ results[i].Add(this.items.Values[i][j].Value);
+ Fx.Assert(results[i].Count == j + 1, "We are adding item for each value in the collection");
+ }
+ Fx.Assert(results[i].Count == this.items.Values[i].Count, "We were supposed to add an item for each value in the collection");
+ }
+ Fx.Assert(results.Count == this.items.Values.Count, "We were supposed to add a sub-list for each values collection");
+ return results;
+ }
+ }
+
+ public void Add(UriTemplateCompoundPathSegment segment, T value)
+ {
+ int index = this.items.IndexOfKey(segment);
+ if (index == -1)
+ {
+ Collection subItems = new Collection();
+ subItems.Add(new CollectionItem(segment, value));
+ this.items.Add(segment, subItems);
+ }
+ else
+ {
+ Collection subItems = this.items.Values[index];
+ subItems.Add(new CollectionItem(segment, value));
+ }
+ }
+
+ public T Find(UriTemplateCompoundPathSegment segment)
+ {
+ int index = this.items.IndexOfKey(segment);
+ if (index == -1)
+ {
+ return null;
+ }
+ Collection subItems = this.items.Values[index];
+ for (int i = 0; i < subItems.Count; i++)
+ {
+ if (subItems[i].Segment.IsEquivalentTo(segment, false))
+ {
+ return subItems[i].Value;
+ }
+ }
+ return null;
+ }
+ public IList> Find(UriTemplateLiteralPathSegment wireData)
+ {
+ IList> results = new List>();
+ for (int i = 0; i < this.items.Values.Count; i++)
+ {
+ List sameOrderResults = null;
+ for (int j = 0; j < this.items.Values[i].Count; j++)
+ {
+ if (this.items.Values[i][j].Segment.IsMatch(wireData))
+ {
+ if (sameOrderResults == null)
+ {
+ sameOrderResults = new List();
+ }
+ sameOrderResults.Add(this.items.Values[i][j].Value);
+ }
+ }
+ if (sameOrderResults != null)
+ {
+ results.Add(sameOrderResults);
+ }
+ }
+ return results;
+ }
+
+ public T GetAnyValue()
+ {
+ if (this.items.Values.Count > 0)
+ {
+ Fx.Assert(this.items.Values[0].Count > 0, "We are not adding a sub-list unless there is at list one item");
+ return this.items.Values[0][0].Value;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public static bool Lookup(AscendingSortedCompoundSegmentsCollection collection,
+ UriTemplateLiteralPathSegment wireData, out IList> results)
+ {
+ results = collection.Find(wireData);
+ return ((results != null) && (results.Count > 0));
+ }
+
+ struct CollectionItem
+ {
+ UriTemplateCompoundPathSegment segment;
+ T value;
+
+ public CollectionItem(UriTemplateCompoundPathSegment segment, T value)
+ {
+ this.segment = segment;
+ this.value = value;
+ }
+
+ public UriTemplateCompoundPathSegment Segment
+ {
+ get
+ {
+ return this.segment;
+ }
+ }
+ public T Value
+ {
+ get
+ {
+ return this.value;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateVariablePathSegment.cs b/src/System.ServiceModel.Web/src/System/UriTemplateVariablePathSegment.cs
new file mode 100644
index 00000000000..72fc9959355
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateVariablePathSegment.cs
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Specialized;
+ using System.Runtime;
+ using System.Text;
+
+ class UriTemplateVariablePathSegment : UriTemplatePathSegment
+ {
+ readonly string varName;
+
+ public UriTemplateVariablePathSegment(string originalSegment, bool endsWithSlash, string varName)
+ : base(originalSegment, UriTemplatePartType.Variable, endsWithSlash)
+ {
+ Fx.Assert(!string.IsNullOrEmpty(varName), "bad variable segment");
+ this.varName = varName;
+ }
+
+ public string VarName
+ {
+ get
+ {
+ return this.varName;
+ }
+ }
+ public override void Bind(string[] values, ref int valueIndex, StringBuilder path)
+ {
+ Fx.Assert(valueIndex < values.Length, "Not enough values to bind");
+ if (this.EndsWithSlash)
+ {
+ path.AppendFormat("{0}/", values[valueIndex++]);
+ }
+ else
+ {
+ path.Append(values[valueIndex++]);
+ }
+ }
+
+ public override bool IsEquivalentTo(UriTemplatePathSegment other, bool ignoreTrailingSlash)
+ {
+ if (other == null)
+ {
+ Fx.Assert("why would we ever call this?");
+ return false;
+ }
+ if (!ignoreTrailingSlash && (this.EndsWithSlash != other.EndsWithSlash))
+ {
+ return false;
+ }
+ return (other.Nature == UriTemplatePartType.Variable);
+ }
+ public override bool IsMatch(UriTemplateLiteralPathSegment segment, bool ignoreTrailingSlash)
+ {
+ if (!ignoreTrailingSlash && (this.EndsWithSlash != segment.EndsWithSlash))
+ {
+ return false;
+ }
+ return (!segment.IsNullOrEmpty());
+ }
+ public override void Lookup(string segment, NameValueCollection boundParameters)
+ {
+ Fx.Assert(!string.IsNullOrEmpty(segment), "How can that be? Lookup is expected to be called after IsMatch");
+ boundParameters.Add(this.varName, segment);
+ }
+ }
+}
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateVariableQueryValue.cs b/src/System.ServiceModel.Web/src/System/UriTemplateVariableQueryValue.cs
new file mode 100644
index 00000000000..d493cdc4cc6
--- /dev/null
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateVariableQueryValue.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+namespace System
+{
+ using System.Collections.Specialized;
+ using System.Runtime;
+ using System.ServiceModel.Channels;
+ using System.Text;
+
+ class UriTemplateVariableQueryValue : UriTemplateQueryValue
+ {
+ readonly string varName;
+
+ public UriTemplateVariableQueryValue(string varName)
+ : base(UriTemplatePartType.Variable)
+ {
+ Fx.Assert(!string.IsNullOrEmpty(varName), "bad variable segment");
+ this.varName = varName;
+ }
+ public override void Bind(string keyName, string[] values, ref int valueIndex, StringBuilder query)
+ {
+ Fx.Assert(valueIndex < values.Length, "Not enough values to bind");
+ if (values[valueIndex] == null)
+ {
+ valueIndex++;
+ }
+ else
+ {
+ query.AppendFormat("&{0}={1}", UrlUtility.UrlEncode(keyName, Encoding.UTF8), UrlUtility.UrlEncode(values[valueIndex++], Encoding.UTF8));
+ }
+ }
+
+ public override bool IsEquivalentTo(UriTemplateQueryValue other)
+ {
+ if (other == null)
+ {
+ Fx.Assert("why would we ever call this?");
+ return false;
+ }
+ return (other.Nature == UriTemplatePartType.Variable);
+ }
+ public override void Lookup(string value, NameValueCollection boundParameters)
+ {
+ boundParameters.Add(this.varName, value);
+ }
+ }
+}
From 01301cd5707c0a91bfa45e477028f9f149b8de50 Mon Sep 17 00:00:00 2001
From: Copilot <223556219+Copilot@users.noreply.github.com>
Date: Sat, 13 Jun 2026 20:23:14 +0300
Subject: [PATCH 03/22] Suppress SYSLIB0051 on UriTemplateMatchException
serialization ctor
The .NET FX reference source provides the protected SerializationInfo
constructor for binary-formatter compat. On .NET 8+ this constructor is
obsolete (SYSLIB0051). Mark with [Obsolete(DiagnosticId = "SYSLIB0051")]
to pin the obsolescence inline and let it pass under -warnaserror.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../src/System/UriTemplateMatchException.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/System.ServiceModel.Web/src/System/UriTemplateMatchException.cs b/src/System.ServiceModel.Web/src/System/UriTemplateMatchException.cs
index f0d20acdb75..b985a411bd9 100644
--- a/src/System.ServiceModel.Web/src/System/UriTemplateMatchException.cs
+++ b/src/System.ServiceModel.Web/src/System/UriTemplateMatchException.cs
@@ -21,6 +21,7 @@ public UriTemplateMatchException(string message, Exception innerException)
: base(message, innerException)
{
}
+ [Obsolete(DiagnosticId = "SYSLIB0051")] // add this attribute to the serialization ctor
protected UriTemplateMatchException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
From 87a6a474af2df4be88d1201c1631321bca216919 Mon Sep 17 00:00:00 2001
From: Copilot <223556219+Copilot@users.noreply.github.com>
Date: Sat, 13 Jun 2026 20:25:46 +0300
Subject: [PATCH 04/22] Demote MSB3243 to message in
System.ServiceModel.Web.Tests csproj
Our new System.ServiceModel.Web v10 collides at reference resolution
time with the BCL shim System.ServiceModel.Web v4.0.0.0 (the one that
type-forwards only Syndication and JSON types). MSBuild emits MSB3243
("No way to resolve conflict"), arbitrarily picking our v10 - which is
the correct outcome - but the warning is escalated to an error under
-warnaserror.
Suppress the noise by adding MSB3243 to MSBuildWarningsAsMessages on
the test project only. The conflict only exists in test reference
closure (Infrastructure.Common pulls in shim transitively); the impl
project does not see it.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../tests/System.ServiceModel.Web.Tests.csproj | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/System.ServiceModel.Web/tests/System.ServiceModel.Web.Tests.csproj b/src/System.ServiceModel.Web/tests/System.ServiceModel.Web.Tests.csproj
index 92510aeb0c5..55382c2b2ff 100644
--- a/src/System.ServiceModel.Web/tests/System.ServiceModel.Web.Tests.csproj
+++ b/src/System.ServiceModel.Web/tests/System.ServiceModel.Web.Tests.csproj
@@ -4,6 +4,11 @@
falsetruefalse
+
+ $(MSBuildWarningsAsMessages);MSB3243
From 16e9b64382d512d262c5fce31e6630941b2eff81 Mon Sep 17 00:00:00 2001
From: Copilot <223556219+Copilot@users.noreply.github.com>
Date: Sat, 13 Jun 2026 23:30:08 +0300
Subject: [PATCH 05/22] Port Phase 3: encoding plumbing from CoreWCF.WebHttp
v1.8.0
Lifts the WebMessage / Json encoder factories and related helpers from
the MIT-licensed CoreWCF.WebHttp v1.8.0 source (CoreWCF/CoreWCF on GitHub).
CoreWCF already adapted this code from the .NET Framework reference source
to .NET Core conventions, so we avoid re-doing that work.
Files added (src/System.ServiceModel.Web/src/System/ServiceModel/Channels/):
- WebMessageEncodingBindingElement.cs - composes the WebMessageEncoder
into a Binding. Surgically stripped IWsdlExportExtension/IWmiInstanceProvider
and BuildChannelListener overrides (server-side only).
- WebMessageEncoderFactory.cs - factory + WebMessageEncoder that demuxes
JSON / XML / Raw based on WebBodyFormatMessageProperty.
- JsonMessageEncoderFactory.cs - factory + JsonMessageEncoder using
DataContractJsonSerializer.
- WebBodyFormatMessageProperty.cs - per-message format tag.
- WebContentFormat.cs + WebContentFormatHelper.cs - Json/Xml/Raw/Default enum.
- WebContentTypeMapper.cs - abstract base for Content-Type to format mapping.
- HttpStreamMessage.cs - wraps Stream as Message for raw pass-through.
- ContentEncoding.cs - charset to encoding mapping struct.
- ContentTypeHelpers.cs - new client subset of CoreWCF helper.
- JsonGlobals.cs - new minimal media-type constants.
- MessageExtensions.cs - helpers for property lookup.
Plus src/System.ServiceModel.Web/src/System/ServiceModel/Pool.cs:
- Internal non-threadsafe object pool (mirror of
System.ServiceModel.NetFramingBase/src/.../Pool.cs).
Adaptations applied while porting CoreWCF source:
- Namespace rewrite: CoreWCF.* -> System.ServiceModel.*
- BCL types resolved via System.* namespaces.
- Access modifiers: protected override -> internal override on members
whose Primitives base is internal (CheckEncodingVersion, IsMatch,
IsCharSetSupported, RecycledMessageState, GetReaderAtHeader).
- Async return types: Task / Task -> ValueTask / ValueTask
to match Primitives' MessageEncoder.ReadMessageAsync/WriteMessageAsync.
- Added sync ReadMessage(Stream,...) and WriteMessage(Message, Stream)
overrides that wrap the async versions via GetAwaiter().GetResult().
- TraceUtility.ThrowHelperError -> DiagnosticUtility.ExceptionUtility.ThrowHelperError
(same signature, available via InternalsVisibleTo to Primitives).
- Deleted duplicates of types already in Primitives: BufferedMessage,
BufferedMessageData, BufferedMessageWriter, EncoderHelpers, XmlAttributeHolder,
ReceivedMessage, BufferManagerOutputStream, StreamBodyWriter. Now resolved via
InternalsVisibleTo (was producing CS0436 warnings).
- TransportDefaults.cs deleted (EncoderDefaults/TextEncoderDefaults/
TransportDefaults already in Primitives).
- RawMessageEncoder getter throws NotSupportedException (raw octet-stream
pass-through requires ByteStreamMessageEncodingBindingElement which is
not yet in dotnet/wcf - deferred to a follow-up).
19 new SR strings added to Resources/Strings.resx covering all error paths
in the new code; 13 XLF translations auto-generated.
Build: full repo, 0 warnings, 0 errors.
Part of porting WebHttpBinding to .NET (Core) WCF client (issues #46, #1413).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../src/Resources/Strings.resx | 54 ++
.../src/Resources/xlf/Strings.cs.xlf | 90 +++
.../src/Resources/xlf/Strings.de.xlf | 90 +++
.../src/Resources/xlf/Strings.es.xlf | 90 +++
.../src/Resources/xlf/Strings.fr.xlf | 90 +++
.../src/Resources/xlf/Strings.it.xlf | 90 +++
.../src/Resources/xlf/Strings.ja.xlf | 90 +++
.../src/Resources/xlf/Strings.ko.xlf | 90 +++
.../src/Resources/xlf/Strings.pl.xlf | 90 +++
.../src/Resources/xlf/Strings.pt-BR.xlf | 90 +++
.../src/Resources/xlf/Strings.ru.xlf | 90 +++
.../src/Resources/xlf/Strings.tr.xlf | 90 +++
.../src/Resources/xlf/Strings.zh-Hans.xlf | 90 +++
.../src/Resources/xlf/Strings.zh-Hant.xlf | 90 +++
.../ServiceModel/Channels/ContentEncoding.cs | 13 +
.../Channels/ContentTypeHelpers.cs | 58 ++
.../Channels/HttpStreamMessage.cs | 203 +++++++
.../ServiceModel/Channels/JsonGlobals.cs | 14 +
.../Channels/JsonMessageEncoderFactory.cs | 529 ++++++++++++++++++
.../Channels/MessageExtensions.cs | 15 +
.../Channels/WebBodyFormatMessageProperty.cs | 72 +++
.../ServiceModel/Channels/WebContentFormat.cs | 13 +
.../Channels/WebContentFormatHelper.cs | 16 +
.../Channels/WebContentTypeMapper.cs | 10 +
.../Channels/WebMessageEncoderFactory.cs | 419 ++++++++++++++
.../WebMessageEncodingBindingElement.cs | 175 ++++++
.../src/System/ServiceModel/Pool.cs | 56 ++
27 files changed, 2817 insertions(+)
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/ContentEncoding.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/ContentTypeHelpers.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/HttpStreamMessage.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/JsonGlobals.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/JsonMessageEncoderFactory.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/MessageExtensions.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/WebBodyFormatMessageProperty.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/WebContentFormat.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/WebContentFormatHelper.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/WebContentTypeMapper.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/WebMessageEncoderFactory.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Channels/WebMessageEncodingBindingElement.cs
create mode 100644 src/System.ServiceModel.Web/src/System/ServiceModel/Pool.cs
diff --git a/src/System.ServiceModel.Web/src/Resources/Strings.resx b/src/System.ServiceModel.Web/src/Resources/Strings.resx
index f72f8ffc0c4..bd66bea39c7 100644
--- a/src/System.ServiceModel.Web/src/Resources/Strings.resx
+++ b/src/System.ServiceModel.Web/src/Resources/Strings.resx
@@ -189,4 +189,58 @@
UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+ WebContentFormat.Default cannot be set on a WebBodyFormatMessageProperty; use Xml, Json, or Raw instead.
+
+
+ The envelope version of the outgoing message ({0}) does not match the envelope version of the encoder ({1}).
+
+
+ The WebContentTypeMapper threw an exception while attempting to map the content type. See the inner exception for details.
+
+
+ WebMessageEncodingBindingElement only supports MessageVersion.None.
+
+
+ The value must be in the range [{0}, {1}].
+
+
+ The maximum message size quota for outgoing messages ({0}) has been exceeded.
+
+
+ The message body is a Stream and cannot be re-read.
+
+
+ The message body is missing or empty.
+
+
+ The XmlReader used to read the message body is in an invalid read state '{0}'.
+
+
+ The MessageBuffer has been closed and can no longer be used.
+
+
+ The message has been closed and can no longer be used.
+
+
+ Unknown MessageVersion.
+
+
+ Unknown WebContentFormat '{0}'.
+
+
+ The value supplied must be non-negative.
+
+
+ The value supplied must be greater than zero.
+
+
+ WebBodyFormatMessageProperty(Format={0})
+
+
+ Raw (application/octet-stream pass-through) content is not yet supported by the WebHttpBinding client. Use Xml or Json instead, or convert your payload to a typed parameter.
+
+
+ The XmlBuffer's quota has been exceeded.
+
diff --git a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.cs.xlf b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.cs.xlf
index 8839e3a6eaa..a013089d116 100644
--- a/src/System.ServiceModel.Web/src/Resources/xlf/Strings.cs.xlf
+++ b/src/System.ServiceModel.Web/src/Resources/xlf/Strings.cs.xlf
@@ -7,6 +7,66 @@
Bind parameters cannot be null or empty for path variable '{0}'.
+
+ WebContentFormat.Default cannot be set on a WebBodyFormatMessageProperty; use Xml, Json, or Raw instead.
+ WebContentFormat.Default cannot be set on a WebBodyFormatMessageProperty; use Xml, Json, or Raw instead.
+
+
+
+ The envelope version of the outgoing message ({0}) does not match the envelope version of the encoder ({1}).
+ The envelope version of the outgoing message ({0}) does not match the envelope version of the encoder ({1}).
+
+
+
+ The WebContentTypeMapper threw an exception while attempting to map the content type. See the inner exception for details.
+ The WebContentTypeMapper threw an exception while attempting to map the content type. See the inner exception for details.
+
+
+
+ WebMessageEncodingBindingElement only supports MessageVersion.None.
+ WebMessageEncodingBindingElement only supports MessageVersion.None.
+
+
+
+ The value must be in the range [{0}, {1}].
+ The value must be in the range [{0}, {1}].
+
+
+
+ The maximum message size quota for outgoing messages ({0}) has been exceeded.
+ The maximum message size quota for outgoing messages ({0}) has been exceeded.
+
+
+
+ The message body is a Stream and cannot be re-read.
+ The message body is a Stream and cannot be re-read.
+
+
+
+ The message body is missing or empty.
+ The message body is missing or empty.
+
+
+
+ The XmlReader used to read the message body is in an invalid read state '{0}'.
+ The XmlReader used to read the message body is in an invalid read state '{0}'.
+
+
+
+ The MessageBuffer has been closed and can no longer be used.
+ The MessageBuffer has been closed and can no longer be used.
+
+
+
+ The message has been closed and can no longer be used.
+ The message has been closed and can no longer be used.
+
+
+
+ Unknown MessageVersion.
+ Unknown MessageVersion.
+
+ Object is read-only.Object is read-only.
@@ -212,6 +272,36 @@
UriTemplate '{0}' has multiple variables with the name '{1}'. Variable names must be unique within a template.
+
+ Unknown WebContentFormat '{0}'.
+ Unknown WebContentFormat '{0}'.
+
+
+
+ The value supplied must be non-negative.
+ The value supplied must be non-negative.
+
+
+
+ The value supplied must be greater than zero.
+ The value supplied must be greater than zero.
+
+
+
+ WebBodyFormatMessageProperty(Format={0})
+ WebBodyFormatMessageProperty(Format={0})
+
+
+
+ Raw (application/octet-stream pass-through) content is not yet supported by the WebHttpBinding client. Use Xml or Json instead, or convert your payload to a typed parameter.
+ Raw (application/octet-stream pass-through) content is not yet supported by the WebHttpBinding client. Use Xml or Json instead, or convert your payload to a typed parameter.
+
+
+
+ The XmlBuffer's quota has been exceeded.
+ The XmlBuffer's quota has been exceeded.
+
+