Skip to content

Commit c9ea20e

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

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
@@ -1119,7 +1119,7 @@ public static Collection<FunctionMetadata> ReadFunctionMetadata(ScriptHostConfig
11191119
continue;
11201120
}
11211121

1122-
ValidateFunctionName(functionName);
1122+
ValidateName(functionName);
11231123

11241124
string json = File.ReadAllText(functionConfigPath);
11251125
JObject functionConfig = JObject.Parse(json);
@@ -1196,27 +1196,38 @@ private Collection<FunctionMetadata> LoadProxyRoutes(string proxiesJson)
11961196

11971197
foreach (var route in routes.Routes)
11981198
{
1199-
var proxyMetadata = new FunctionMetadata();
1200-
1201-
var json = new JObject
1199+
try
12021200
{
1203-
{ "authLevel", "anonymous" },
1204-
{ "name", "req" },
1205-
{ "type", "httptrigger" },
1206-
{ "direction", "in" },
1207-
{ "Route", route.UrlTemplate.TrimStart('/') },
1208-
{ "Methods", new JArray(route.Methods.Select(m => m.Method.ToString()).ToArray()) }
1209-
};
1201+
// Proxy names should follow the same naming restrictions as in function names.
1202+
ValidateName(route.Name, true);
12101203

1211-
BindingMetadata bindingMetadata = BindingMetadata.Create(json);
1204+
var proxyMetadata = new FunctionMetadata();
12121205

1213-
proxyMetadata.Bindings.Add(bindingMetadata);
1206+
var json = new JObject
1207+
{
1208+
{ "authLevel", "anonymous" },
1209+
{ "name", "req" },
1210+
{ "type", "httptrigger" },
1211+
{ "direction", "in" },
1212+
{ "Route", route.UrlTemplate.TrimStart('/') },
1213+
{ "Methods", new JArray(route.Methods.Select(m => m.Method.ToString()).ToArray()) }
1214+
};
12141215

1215-
proxyMetadata.Name = route.Name;
1216-
proxyMetadata.ScriptType = ScriptType.Unknown;
1217-
proxyMetadata.IsProxy = true;
1216+
BindingMetadata bindingMetadata = BindingMetadata.Create(json);
12181217

1219-
proxies.Add(proxyMetadata);
1218+
proxyMetadata.Bindings.Add(bindingMetadata);
1219+
1220+
proxyMetadata.Name = route.Name;
1221+
proxyMetadata.ScriptType = ScriptType.Unknown;
1222+
proxyMetadata.IsProxy = true;
1223+
1224+
proxies.Add(proxyMetadata);
1225+
}
1226+
catch (Exception ex)
1227+
{
1228+
// log any unhandled exceptions and continue
1229+
AddFunctionError(FunctionErrors, route.Name, Utility.FlattenException(ex, includeSource: false), isFunctionShortName: true);
1230+
}
12201231
}
12211232

12221233
return proxies;
@@ -1263,11 +1274,11 @@ internal static bool TryParseFunctionMetadata(string functionName, JObject funct
12631274
return true;
12641275
}
12651276

1266-
internal static void ValidateFunctionName(string functionName)
1277+
internal static void ValidateName(string name, bool isProxy = false)
12671278
{
1268-
if (!FunctionNameValidationRegex.IsMatch(functionName))
1279+
if (!FunctionNameValidationRegex.IsMatch(name))
12691280
{
1270-
throw new InvalidOperationException(string.Format("'{0}' is not a valid function name.", functionName));
1281+
throw new InvalidOperationException(string.Format("'{0}' is not a valid {1} name.", name, isProxy ? "proxy" : "function"));
12711282
}
12721283
}
12731284

@@ -1437,6 +1448,11 @@ internal static void ValidateFunction(FunctionDescriptor function, Dictionary<st
14371448
}
14381449
}
14391450

1451+
if (httpFunctions.ContainsKey(function.Name))
1452+
{
1453+
throw new InvalidOperationException($"The function or proxy name '{function.Name}' must be unique within the function app.");
1454+
}
1455+
14401456
httpFunctions.Add(function.Name, httpTrigger);
14411457
}
14421458
}

test/WebJobs.Script.Tests/ScriptHostTests.cs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,12 +1230,25 @@ public void ValidateFunctionName_ThrowsOnInvalidName(string functionName)
12301230
{
12311231
var ex = Assert.Throws<InvalidOperationException>(() =>
12321232
{
1233-
ScriptHost.ValidateFunctionName(functionName);
1233+
ScriptHost.ValidateName(functionName);
12341234
});
12351235

12361236
Assert.Equal(string.Format("'{0}' is not a valid function name.", functionName), ex.Message);
12371237
}
12381238

1239+
[Theory]
1240+
[InlineData("bing.com")]
1241+
[InlineData("http://bing.com")]
1242+
public void ValidateProxyName_ThrowsOnInvalidName(string proxyName)
1243+
{
1244+
var ex = Assert.Throws<InvalidOperationException>(() =>
1245+
{
1246+
ScriptHost.ValidateName(proxyName, true);
1247+
});
1248+
1249+
Assert.Equal(string.Format("'{0}' is not a valid proxy name.", proxyName), ex.Message);
1250+
}
1251+
12391252
[Theory]
12401253
[InlineData("testwithhost")]
12411254
[InlineData("hosts")]
@@ -1246,7 +1259,7 @@ public void ValidateFunctionName_DoesNotThrowOnValidName(string functionName)
12461259
{
12471260
try
12481261
{
1249-
ScriptHost.ValidateFunctionName(functionName);
1262+
ScriptHost.ValidateName(functionName);
12501263
}
12511264
catch (InvalidOperationException)
12521265
{
@@ -1394,6 +1407,40 @@ public void ValidateFunction_ValidatesHttpRoutes()
13941407
}
13951408
#endif
13961409

1410+
[Fact]
1411+
public void ValidateFunction_ThrowsOnDuplicateName()
1412+
{
1413+
var httpFunctions = new Dictionary<string, HttpTriggerAttribute>();
1414+
var name = "test";
1415+
1416+
// first add an http function
1417+
var metadata = new FunctionMetadata();
1418+
var function = new Mock<FunctionDescriptor>(MockBehavior.Strict, name, null, metadata, null, null, null, null);
1419+
var attribute = new HttpTriggerAttribute(AuthorizationLevel.Function, "get");
1420+
function.Setup(p => p.GetTriggerAttributeOrNull<HttpTriggerAttribute>()).Returns(() => attribute);
1421+
1422+
ScriptHost.ValidateFunction(function.Object, httpFunctions);
1423+
1424+
// add a proxy with same name
1425+
metadata = new FunctionMetadata()
1426+
{
1427+
IsProxy = true
1428+
};
1429+
function = new Mock<FunctionDescriptor>(MockBehavior.Strict, name, null, metadata, null, null, null, null);
1430+
attribute = new HttpTriggerAttribute(AuthorizationLevel.Function, "get")
1431+
{
1432+
Route = "proxyRoute"
1433+
};
1434+
function.Setup(p => p.GetTriggerAttributeOrNull<HttpTriggerAttribute>()).Returns(() => attribute);
1435+
1436+
var ex = Assert.Throws<InvalidOperationException>(() =>
1437+
{
1438+
ScriptHost.ValidateFunction(function.Object, httpFunctions);
1439+
});
1440+
1441+
Assert.Equal(string.Format($"The function or proxy name '{name}' must be unique within the function app.", name), ex.Message);
1442+
}
1443+
13971444
[Fact]
13981445
public void IsFunction_ReturnsExpectedResult()
13991446
{

0 commit comments

Comments
 (0)