Skip to content

Commit bdf0c61

Browse files
committed
Proxy names should follow the same naming restrictions as function names.
#2010 Azure/azure-webjobs-sdk#1368
1 parent 07d99be commit bdf0c61

File tree

2 files changed

+85
-22
lines changed

2 files changed

+85
-22
lines changed

src/WebJobs.Script/Host/ScriptHost.cs

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,7 @@ public static Collection<FunctionMetadata> ReadFunctionMetadata(ScriptHostConfig
922922
continue;
923923
}
924924

925-
ValidateFunctionName(functionName);
925+
ValidateName(functionName);
926926

927927
string json = File.ReadAllText(functionConfigPath);
928928
JObject functionConfig = JObject.Parse(json);
@@ -999,27 +999,38 @@ private Collection<FunctionMetadata> LoadProxyRoutes(string proxiesJson)
999999

10001000
foreach (var route in routes.Routes)
10011001
{
1002-
var proxyMetadata = new FunctionMetadata();
1003-
1004-
var json = new JObject
1002+
try
10051003
{
1006-
{ "authLevel", "anonymous" },
1007-
{ "name", "req" },
1008-
{ "type", "httptrigger" },
1009-
{ "direction", "in" },
1010-
{ "Route", route.UrlTemplate.TrimStart('/') },
1011-
{ "Methods", new JArray(route.Methods.Select(m => m.Method.ToString()).ToArray()) }
1012-
};
1004+
// Proxy names should follow the same naming restrictions as in function names.
1005+
ValidateName(route.Name, true);
10131006

1014-
BindingMetadata bindingMetadata = BindingMetadata.Create(json);
1007+
var proxyMetadata = new FunctionMetadata();
10151008

1016-
proxyMetadata.Bindings.Add(bindingMetadata);
1009+
var json = new JObject
1010+
{
1011+
{ "authLevel", "anonymous" },
1012+
{ "name", "req" },
1013+
{ "type", "httptrigger" },
1014+
{ "direction", "in" },
1015+
{ "Route", route.UrlTemplate.TrimStart('/') },
1016+
{ "Methods", new JArray(route.Methods.Select(m => m.Method.ToString()).ToArray()) }
1017+
};
10171018

1018-
proxyMetadata.Name = route.Name;
1019-
proxyMetadata.ScriptType = ScriptType.Unknown;
1020-
proxyMetadata.IsProxy = true;
1019+
BindingMetadata bindingMetadata = BindingMetadata.Create(json);
10211020

1022-
proxies.Add(proxyMetadata);
1021+
proxyMetadata.Bindings.Add(bindingMetadata);
1022+
1023+
proxyMetadata.Name = route.Name;
1024+
proxyMetadata.ScriptType = ScriptType.Unknown;
1025+
proxyMetadata.IsProxy = true;
1026+
1027+
proxies.Add(proxyMetadata);
1028+
}
1029+
catch (Exception ex)
1030+
{
1031+
// log any unhandled exceptions and continue
1032+
AddFunctionError(FunctionErrors, route.Name, Utility.FlattenException(ex, includeSource: false), isFunctionShortName: true);
1033+
}
10231034
}
10241035

10251036
return proxies;
@@ -1087,11 +1098,11 @@ internal static bool HttpRoutesConflict(HttpTriggerAttribute httpTrigger, HttpTr
10871098
return httpTrigger.Methods.Intersect(otherHttpTrigger.Methods).Any();
10881099
}
10891100

1090-
internal static void ValidateFunctionName(string functionName)
1101+
internal static void ValidateName(string name, bool isProxy = false)
10911102
{
1092-
if (!FunctionNameValidationRegex.IsMatch(functionName))
1103+
if (!FunctionNameValidationRegex.IsMatch(name))
10931104
{
1094-
throw new InvalidOperationException(string.Format("'{0}' is not a valid function name.", functionName));
1105+
throw new InvalidOperationException(string.Format("'{0}' is not a valid {1} name.", name, isProxy ? "proxy" : "function"));
10951106
}
10961107
}
10971108

@@ -1277,6 +1288,11 @@ internal static void ValidateFunction(FunctionDescriptor function, Dictionary<st
12771288
}
12781289
}
12791290

1291+
if (httpFunctions.ContainsKey(function.Name))
1292+
{
1293+
throw new InvalidOperationException($"The function or proxy name '{function.Name}' must be unique within the function app.");
1294+
}
1295+
12801296
httpFunctions.Add(function.Name, httpTrigger);
12811297
}
12821298
}

test/WebJobs.Script.Tests/ScriptHostTests.cs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,12 +1271,25 @@ public void ValidateFunctionName_ThrowsOnInvalidName(string functionName)
12711271
{
12721272
var ex = Assert.Throws<InvalidOperationException>(() =>
12731273
{
1274-
ScriptHost.ValidateFunctionName(functionName);
1274+
ScriptHost.ValidateName(functionName);
12751275
});
12761276

12771277
Assert.Equal(string.Format("'{0}' is not a valid function name.", functionName), ex.Message);
12781278
}
12791279

1280+
[Theory]
1281+
[InlineData("bing.com")]
1282+
[InlineData("http://bing.com")]
1283+
public void ValidateProxyName_ThrowsOnInvalidName(string proxyName)
1284+
{
1285+
var ex = Assert.Throws<InvalidOperationException>(() =>
1286+
{
1287+
ScriptHost.ValidateName(proxyName, true);
1288+
});
1289+
1290+
Assert.Equal(string.Format("'{0}' is not a valid proxy name.", proxyName), ex.Message);
1291+
}
1292+
12801293
[Theory]
12811294
[InlineData("testwithhost")]
12821295
[InlineData("hosts")]
@@ -1287,7 +1300,7 @@ public void ValidateFunctionName_DoesNotThrowOnValidName(string functionName)
12871300
{
12881301
try
12891302
{
1290-
ScriptHost.ValidateFunctionName(functionName);
1303+
ScriptHost.ValidateName(functionName);
12911304
}
12921305
catch (InvalidOperationException)
12931306
{
@@ -1433,6 +1446,40 @@ public void ValidateFunction_ValidatesHttpRoutes()
14331446
Assert.Equal("test6", attribute.Route);
14341447
}
14351448

1449+
[Fact]
1450+
public void ValidateFunction_ThrowsOnDuplicateName()
1451+
{
1452+
var httpFunctions = new Dictionary<string, HttpTriggerAttribute>();
1453+
var name = "test";
1454+
1455+
// first add an http function
1456+
var metadata = new FunctionMetadata();
1457+
var function = new Mock<FunctionDescriptor>(MockBehavior.Strict, name, null, metadata, null, null, null, null);
1458+
var attribute = new HttpTriggerAttribute(AuthorizationLevel.Function, "get");
1459+
function.Setup(p => p.GetTriggerAttributeOrNull<HttpTriggerAttribute>()).Returns(() => attribute);
1460+
1461+
ScriptHost.ValidateFunction(function.Object, httpFunctions);
1462+
1463+
// add a proxy with same name
1464+
metadata = new FunctionMetadata()
1465+
{
1466+
IsProxy = true
1467+
};
1468+
function = new Mock<FunctionDescriptor>(MockBehavior.Strict, name, null, metadata, null, null, null, null);
1469+
attribute = new HttpTriggerAttribute(AuthorizationLevel.Function, "get")
1470+
{
1471+
Route = "proxyRoute"
1472+
};
1473+
function.Setup(p => p.GetTriggerAttributeOrNull<HttpTriggerAttribute>()).Returns(() => attribute);
1474+
1475+
var ex = Assert.Throws<InvalidOperationException>(() =>
1476+
{
1477+
ScriptHost.ValidateFunction(function.Object, httpFunctions);
1478+
});
1479+
1480+
Assert.Equal(string.Format($"The function or proxy name '{name}' must be unique within the function app.", name), ex.Message);
1481+
}
1482+
14361483
[Fact]
14371484
public void IsFunction_ReturnsExpectedResult()
14381485
{

0 commit comments

Comments
 (0)