diff --git a/nanoFramework.WebServer.FileSystem/nanoFramework.WebServer.FileSystem.nfproj b/nanoFramework.WebServer.FileSystem/nanoFramework.WebServer.FileSystem.nfproj
index a11dd06..59277d1 100644
--- a/nanoFramework.WebServer.FileSystem/nanoFramework.WebServer.FileSystem.nfproj
+++ b/nanoFramework.WebServer.FileSystem/nanoFramework.WebServer.FileSystem.nfproj
@@ -99,14 +99,13 @@
..\packages\nanoFramework.System.IO.Streams.1.1.52\lib\System.IO.Streams.dll
True
-
- ..\packages\nanoFramework.System.Net.1.10.64\lib\System.Net.dll
- True
-
..\packages\nanoFramework.System.IO.FileSystem.1.1.23\lib\System.IO.FileSystem.dll
True
+
+ ..\packages\nanoFramework.System.Net.1.10.68\lib\System.Net.dll
+
..\packages\nanoFramework.System.Net.Http.Server.1.5.113\lib\System.Net.Http.dll
True
diff --git a/nanoFramework.WebServer.FileSystem/packages.config b/nanoFramework.WebServer.FileSystem/packages.config
index cc8cd99..a67602b 100644
--- a/nanoFramework.WebServer.FileSystem/packages.config
+++ b/nanoFramework.WebServer.FileSystem/packages.config
@@ -5,7 +5,7 @@
-
+
diff --git a/nanoFramework.WebServer.FileSystem/packages.lock.json b/nanoFramework.WebServer.FileSystem/packages.lock.json
index 976ba7d..da1f598 100644
--- a/nanoFramework.WebServer.FileSystem/packages.lock.json
+++ b/nanoFramework.WebServer.FileSystem/packages.lock.json
@@ -34,9 +34,9 @@
},
"nanoFramework.System.Net": {
"type": "Direct",
- "requested": "[1.10.64, 1.10.64]",
- "resolved": "1.10.64",
- "contentHash": "BM/+UzIrPqseGTRO1biYDU5zT5Mnt9Rb2IDa0milqbulAKJtZn0Sv+dwxSrT+FI27q8mgu78azhDF+fCqSmGhQ=="
+ "requested": "[1.10.68, 1.10.68]",
+ "resolved": "1.10.68",
+ "contentHash": "PoM4NadhtMBnuGiOPqi+TJZD8xicz0v72hG5gPGDvWXeKDCMYCMqiCqY+di2pRL3qGHkF+hh8kLOf+wBUJBWNQ=="
},
"nanoFramework.System.Net.Http.Server": {
"type": "Direct",
diff --git a/nanoFramework.WebServer.sln b/nanoFramework.WebServer.sln
index ce816cf..f8a5431 100644
--- a/nanoFramework.WebServer.sln
+++ b/nanoFramework.WebServer.sln
@@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.WebServer.FileSystem", "nanoFramework.WebServer.FileSystem\nanoFramework.WebServer.FileSystem.nfproj", "{9D8A2D18-8036-4880-B46B-D5218247257D}"
EndProject
+Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.WebServer.Tests", "tests\nanoFramework.WebServer.Tests\nanoFramework.WebServer.Tests.nfproj", "{2C2B4750-2A48-4D19-9404-178AAB946482}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -31,6 +33,12 @@ Global
{9D8A2D18-8036-4880-B46B-D5218247257D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D8A2D18-8036-4880-B46B-D5218247257D}.Release|Any CPU.Build.0 = Release|Any CPU
{9D8A2D18-8036-4880-B46B-D5218247257D}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {2C2B4750-2A48-4D19-9404-178AAB946482}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2C2B4750-2A48-4D19-9404-178AAB946482}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2C2B4750-2A48-4D19-9404-178AAB946482}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {2C2B4750-2A48-4D19-9404-178AAB946482}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2C2B4750-2A48-4D19-9404-178AAB946482}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2C2B4750-2A48-4D19-9404-178AAB946482}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/nanoFramework.WebServer/MethodAttribute.cs b/nanoFramework.WebServer/MethodAttribute.cs
index cd08f0a..527a056 100644
--- a/nanoFramework.WebServer/MethodAttribute.cs
+++ b/nanoFramework.WebServer/MethodAttribute.cs
@@ -18,9 +18,9 @@ namespace nanoFramework.WebServer
public class MethodAttribute : Attribute
{
///
- /// Gets or sets the method.
+ /// Gets the method.
///
- public string Method { get; set; }
+ public string Method { get; }
///
/// Creates a method attribute.
diff --git a/nanoFramework.WebServer/Properties/AssemblyInfo.cs b/nanoFramework.WebServer/Properties/AssemblyInfo.cs
index adc9caf..7bae945 100644
--- a/nanoFramework.WebServer/Properties/AssemblyInfo.cs
+++ b/nanoFramework.WebServer/Properties/AssemblyInfo.cs
@@ -15,4 +15,3 @@
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
-
diff --git a/nanoFramework.WebServer/WebServer.cs b/nanoFramework.WebServer/WebServer.cs
index 75538d5..f32ba94 100644
--- a/nanoFramework.WebServer/WebServer.cs
+++ b/nanoFramework.WebServer/WebServer.cs
@@ -177,76 +177,77 @@ public WebServer(int port, HttpProtocol protocol) : this(port, protocol, null)
public WebServer(int port, HttpProtocol protocol, Type[] controllers)
{
_callbackRoutes = new ArrayList();
+ RegisterControllers(controllers);
+ Protocol = protocol;
+ Port = port;
+ string prefix = Protocol == HttpProtocol.Http ? "http" : "https";
+ _listener = new HttpListener(prefix, port);
+ }
- if (controllers != null)
+ private void RegisterControllers(Type[] controllers)
+ {
+ if (controllers == null)
{
- foreach (var controller in controllers)
+ return;
+ }
+
+ foreach (var controller in controllers)
+ {
+ var controlAttribs = controller.GetCustomAttributes(true);
+ Authentication authentication = null;
+ foreach (var ctrlAttrib in controlAttribs)
{
- var controlAttribs = controller.GetCustomAttributes(true);
- Authentication authentication = null;
- foreach (var ctrlAttrib in controlAttribs)
+ if (typeof(AuthenticationAttribute) == ctrlAttrib.GetType())
{
- if (typeof(AuthenticationAttribute) == ctrlAttrib.GetType())
- {
- var strAuth = ((AuthenticationAttribute)ctrlAttrib).AuthenticationMethod;
- // We do support only None, Basic and ApiKey, raising an exception if this doesn't start by any
- authentication = ExtractAuthentication(strAuth);
- }
+ var strAuth = ((AuthenticationAttribute)ctrlAttrib).AuthenticationMethod;
+ // We do support only None, Basic and ApiKey, raising an exception if this doesn't start by any
+ authentication = ExtractAuthentication(strAuth);
}
+ }
- var functions = controller.GetMethods();
- foreach (var func in functions)
+ var functions = controller.GetMethods();
+ foreach (var func in functions)
+ {
+ var attributes = func.GetCustomAttributes(true);
+ foreach (var attrib in attributes)
{
- var attributes = func.GetCustomAttributes(true);
- CallbackRoutes callbackRoutes = null;
- foreach (var attrib in attributes)
+ if (typeof(RouteAttribute) != attrib.GetType())
{
- if (typeof(RouteAttribute) == attrib.GetType())
+ continue;
+ }
+
+ var callbackRoutes = new CallbackRoutes
+ {
+ Route = ((RouteAttribute)attrib).Route,
+ CaseSensitive = false,
+ Method = string.Empty,
+ Authentication = authentication,
+ Callback = func
+ };
+
+ foreach (var attribute in attributes)
+ {
+ if (typeof(MethodAttribute) == attribute.GetType())
+ {
+ callbackRoutes.Method = ((MethodAttribute)attribute).Method;
+ }
+ else if (typeof(CaseSensitiveAttribute) == attribute.GetType())
+ {
+ callbackRoutes.CaseSensitive = true;
+ }
+ else if (typeof(AuthenticationAttribute) == attribute.GetType())
{
- callbackRoutes = new CallbackRoutes();
- callbackRoutes.Route = ((RouteAttribute)attrib).Route;
- callbackRoutes.CaseSensitive = false;
- callbackRoutes.Method = string.Empty;
- callbackRoutes.Authentication = authentication;
-
- callbackRoutes.Callback = func;
- foreach (var otherattrib in attributes)
- {
- if (typeof(MethodAttribute) == otherattrib.GetType())
- {
- callbackRoutes.Method = ((MethodAttribute)otherattrib).Method;
- }
- else if (typeof(CaseSensitiveAttribute) == otherattrib.GetType())
- {
- callbackRoutes.CaseSensitive = true;
- }
- else if (typeof(AuthenticationAttribute) == otherattrib.GetType())
- {
- var strAuth = ((AuthenticationAttribute)otherattrib).AuthenticationMethod;
- // A method can have a different authentication than the main class, so we override if any
- callbackRoutes.Authentication = ExtractAuthentication(strAuth);
- }
- }
-
- _callbackRoutes.Add(callbackRoutes);
+ var strAuth = ((AuthenticationAttribute)attribute).AuthenticationMethod;
+ // A method can have a different authentication than the main class, so we override if any
+ callbackRoutes.Authentication = ExtractAuthentication(strAuth);
}
}
- }
+ _callbackRoutes.Add(callbackRoutes);
+ Debug.WriteLine($"{callbackRoutes.Callback.Name}, {callbackRoutes.Route}, {callbackRoutes.Method}, {callbackRoutes.CaseSensitive}");
+ }
}
}
-
- foreach (var callback in _callbackRoutes)
- {
- var cb = (CallbackRoutes)callback;
- Debug.WriteLine($"{cb.Callback.Name}, {cb.Route}, {cb.Method}, {cb.CaseSensitive}");
- }
-
- Protocol = protocol;
- Port = port;
- string prefix = Protocol == HttpProtocol.Http ? "http" : "https";
- _listener = new HttpListener(prefix, port);
- Debug.WriteLine("Web server started on port " + port.ToString());
}
private Authentication ExtractAuthentication(string strAuth)
@@ -358,7 +359,8 @@ public bool Start()
{
_cancel = false;
_serverThread.Start();
- Debug.WriteLine("Started server in thread " + _serverThread.GetHashCode().ToString());
+ Debug.WriteLine("Web server started on port " + Port);
+ Debug.WriteLine("Started server in thread " + _serverThread.GetHashCode());
}
catch
{ //if there is a problem, maybe due to the fact we did not wait enough
@@ -368,15 +370,6 @@ public bool Start()
return bStarted;
}
- ///
- /// Restart the server.
- ///
- private bool Restart()
- {
- Stop();
- return Start();
- }
-
///
/// Stop the multi threaded server.
///
@@ -504,54 +497,24 @@ private void StartListener()
new Thread(() =>
{
- bool isRoute = false;
- string rawUrl = context.Request.RawUrl;
-
//This is for handling with transitory or bad requests
- if (rawUrl == null)
+ if (context.Request.RawUrl == null)
{
return;
}
- int urlParam = rawUrl.IndexOf(ParamStart);
-
// Variables used only within the "for". They are here for performance reasons
- bool isFound;
- string routeStr;
- int incForSlash;
- string toCompare;
bool mustAuthenticate;
bool isAuthOk;
- //
+ bool isRoute = false;
- foreach (var rt in _callbackRoutes)
+ foreach (CallbackRoutes route in _callbackRoutes)
{
- CallbackRoutes route = (CallbackRoutes)rt;
-
- routeStr = route.Route;
- incForSlash = routeStr.IndexOf('/') == 0 ? 0 : 1;
- toCompare = route.CaseSensitive ? rawUrl : rawUrl.ToLower();
-
- if (urlParam > 0)
- {
- isFound = urlParam == routeStr.Length + incForSlash;
- }
- else
- {
- isFound = toCompare.Length == routeStr.Length + incForSlash;
- }
-
- // Matching the route name
- // Matching the method type
- if (!isFound ||
- (toCompare.IndexOf(routeStr) != incForSlash) ||
- (route.Method != string.Empty && context.Request.HttpMethod != route.Method)
- )
+ if (!IsRouteMatch(route, context.Request.HttpMethod, context.Request.RawUrl))
{
continue;
}
- // Starting a new thread to be able to handle a new request in parallel
isRoute = true;
// Check auth first
@@ -579,31 +542,23 @@ private void StartListener()
}
}
- if (isAuthOk)
- {
- InvokeRoute(route, context);
- }
- else
+ if (!isAuthOk)
{
- if (route.Authentication != null && route.Authentication.AuthenticationType == AuthenticationType.Basic)
+ if (route.Authentication != null &&
+ route.Authentication.AuthenticationType == AuthenticationType.Basic)
{
- context.Response.Headers.Add("WWW-Authenticate", $"Basic realm=\"Access to {routeStr}\"");
+ context.Response.Headers.Add("WWW-Authenticate",
+ $"Basic realm=\"Access to {route.Route}\"");
}
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Response.ContentLength64 = 0;
- }
- // When context has been handed over to WebsocketServer, it will be null at this point
- if (context.Response == null)
- {
- //do nothing this is a websocket that is managed by a websocketserver that is responsible for the context now.
- }
- else
- {
- context.Response.Close();
- context.Close();
+ HandleContextResponse(context);
+ return;
}
+
+ InvokeRoute(route, context);
}
if (!isRoute)
@@ -615,36 +570,11 @@ private void StartListener()
}
else
{
- context.Response.StatusCode = 404;
+ context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.ContentLength64 = 0;
}
- // When context has been handed over to WebsocketServer, it will be null at this point
- if (context.Response == null)
- {
- //do nothing this is a websocket that is managed by a websocketserver that is responsible for the context now.
- }
- else
- {
- try
- {
- context.Response.Close();
- }
- catch
- {
- // Nothing on purpose
- }
-
- try
- {
- context.Close();
- }
- catch
- {
- // Nothing on purpose
- }
-
- }
+ HandleContextResponse(context);
}
}).Start();
@@ -654,6 +584,52 @@ private void StartListener()
_listener.Stop();
}
}
+
+ ///
+ /// Checks if route matches called resource.
+ /// For internal use only.
+ ///
+ /// Route to check.
+ /// Invoked resource method.
+ /// Invoked resource URL.
+ ///
+ public static bool IsRouteMatch(CallbackRoutes route, string method, string rawUrl)
+ {
+ if (route.Method == string.Empty)
+ {
+ return false;
+ }
+
+ if (method != route.Method)
+ {
+ return false;
+ }
+
+ var urlParam = rawUrl.IndexOf(ParamStart);
+ var incForSlash = route.Route.IndexOf('/') == 0 ? 0 : 1;
+ var rawUrlToCompare = route.CaseSensitive ? rawUrl : rawUrl.ToLower();
+ var routeToCompare = route.CaseSensitive ? route.Route : route.Route.ToLower();
+ bool isFound;
+
+ if (urlParam > 0 )
+ {
+ isFound = urlParam == routeToCompare.Length + incForSlash;
+ }
+ else
+ {
+ isFound = rawUrlToCompare.Length == routeToCompare.Length + incForSlash;
+ }
+
+ // Matching the route name
+ // Matching the method type
+ if (!isFound ||
+ (rawUrlToCompare.IndexOf(routeToCompare) != incForSlash))
+ {
+ return false;
+ }
+
+ return true;
+ }
///
/// Method which invokes route. Can be overriden to inject custom logic.
@@ -665,6 +641,34 @@ protected virtual void InvokeRoute(CallbackRoutes route, HttpListenerContext con
route.Callback.Invoke(null, new object[] { new WebServerEventArgs(context) });
}
+ private static void HandleContextResponse(HttpListenerContext context)
+ {
+ // When context has been handed over to WebsocketServer, it will be null at this point
+ // Do nothing this is a websocket that is managed by a websocketserver that is responsible for the context now.
+ if (context.Response == null)
+ {
+ return;
+ }
+
+ try
+ {
+ context.Response.Close();
+ }
+ catch
+ {
+ // Nothing on purpose
+ }
+
+ try
+ {
+ context.Close();
+ }
+ catch
+ {
+ // Nothing on purpose
+ }
+ }
+
private string GetApiKeyFromHeaders(WebHeaderCollection headers)
{
var sec = headers.GetValues("ApiKey");
@@ -683,7 +687,7 @@ private string GetApiKeyFromHeaders(WebHeaderCollection headers)
private void ListInterfaces()
{
NetworkInterface[] ifaces = NetworkInterface.GetAllNetworkInterfaces();
- Debug.WriteLine("Number of Interfaces: " + ifaces.Length.ToString());
+ Debug.WriteLine("Number of Interfaces: " + ifaces.Length);
foreach (NetworkInterface iface in ifaces)
{
Debug.WriteLine("IP: " + iface.IPv4Address + "/" + iface.IPv4SubnetMask);
diff --git a/nanoFramework.WebServer/nanoFramework.WebServer.nfproj b/nanoFramework.WebServer/nanoFramework.WebServer.nfproj
index e583d7c..d681b66 100644
--- a/nanoFramework.WebServer/nanoFramework.WebServer.nfproj
+++ b/nanoFramework.WebServer/nanoFramework.WebServer.nfproj
@@ -67,9 +67,8 @@
..\packages\nanoFramework.System.IO.Streams.1.1.52\lib\System.IO.Streams.dll
True
-
- ..\packages\nanoFramework.System.Net.1.10.64\lib\System.Net.dll
- True
+
+ ..\packages\nanoFramework.System.Net.1.10.68\lib\System.Net.dll
..\packages\nanoFramework.System.Net.Http.Server.1.5.113\lib\System.Net.Http.dll
diff --git a/nanoFramework.WebServer/packages.config b/nanoFramework.WebServer/packages.config
index ababd67..9d71a71 100644
--- a/nanoFramework.WebServer/packages.config
+++ b/nanoFramework.WebServer/packages.config
@@ -4,7 +4,7 @@
-
+
diff --git a/nanoFramework.WebServer/packages.lock.json b/nanoFramework.WebServer/packages.lock.json
index 7496cf8..c51a4c8 100644
--- a/nanoFramework.WebServer/packages.lock.json
+++ b/nanoFramework.WebServer/packages.lock.json
@@ -28,9 +28,9 @@
},
"nanoFramework.System.Net": {
"type": "Direct",
- "requested": "[1.10.64, 1.10.64]",
- "resolved": "1.10.64",
- "contentHash": "BM/+UzIrPqseGTRO1biYDU5zT5Mnt9Rb2IDa0milqbulAKJtZn0Sv+dwxSrT+FI27q8mgu78azhDF+fCqSmGhQ=="
+ "requested": "[1.10.68, 1.10.68]",
+ "resolved": "1.10.68",
+ "contentHash": "PoM4NadhtMBnuGiOPqi+TJZD8xicz0v72hG5gPGDvWXeKDCMYCMqiCqY+di2pRL3qGHkF+hh8kLOf+wBUJBWNQ=="
},
"nanoFramework.System.Net.Http.Server": {
"type": "Direct",
diff --git a/tests/nanoFramework.WebServer.Tests/Properties/AssemblyInfo.cs b/tests/nanoFramework.WebServer.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..a3735af
--- /dev/null
+++ b/tests/nanoFramework.WebServer.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,31 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyCopyright("Copyright (c) 2021 nanoFramework contributors")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/tests/nanoFramework.WebServer.Tests/WebServerTests.cs b/tests/nanoFramework.WebServer.Tests/WebServerTests.cs
new file mode 100644
index 0000000..3b4cbf0
--- /dev/null
+++ b/tests/nanoFramework.WebServer.Tests/WebServerTests.cs
@@ -0,0 +1,91 @@
+//
+// Copyright (c) 2020 Laurent Ellerbach and the project contributors
+// See LICENSE file in the project root for full license information.
+//
+
+using nanoFramework.TestFramework;
+using System;
+
+namespace nanoFramework.WebServer.Tests
+{
+ [TestClass]
+ public class WebServerTests
+ {
+ [TestMethod]
+ public void IsRouteMatch_Should_ReturnFalseForEmptyMethod()
+ {
+ // Arrange
+ var route = new CallbackRoutes()
+ {
+ Method = "GET",
+ Route = "/api/test"
+ };
+
+ // Act
+ var result = WebServer.IsRouteMatch(route, "", "/api/test");
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ [TestMethod]
+ public void IsRouteMatch_Should_ReturnFalseForNotMatchingMethod()
+ {
+ // Arrange
+ var route = new CallbackRoutes()
+ {
+ Method = "GET",
+ Route = "/api/test"
+ };
+
+ // Act
+ var result = WebServer.IsRouteMatch(route, "POST", "/api/test");
+
+ // Assert
+ Assert.IsFalse(result);
+ }
+
+ [TestMethod]
+ [DataRow("GET", "/api/test", "/api/test")]
+ [DataRow("POST", "/api/test", "/api/test")]
+ [DataRow("PUT", "/api/test", "/api/test")]
+ [DataRow("PATCH", "/api/test", "/api/test")]
+ [DataRow("DELETE", "/api/test", "/api/test")]
+ [DataRow("GET", "/API/TEST", "/api/test")]
+ [DataRow("POST", "/API/TEST", "/api/test")]
+ [DataRow("PUT", "/API/TEST", "/api/test")]
+ [DataRow("PATCH", "/API/TEST", "/api/test")]
+ [DataRow("DELETE", "/API/TEST", "/api/test")]
+ [DataRow("GET", "/api/test", "/API/TEST")]
+ [DataRow("POST", "/api/test", "/api/test")]
+ [DataRow("PUT", "/api/test", "/API/TEST")]
+ [DataRow("PATCH", "/api/test", "/API/TEST")]
+ [DataRow("DELETE", "/api/test", "/API/TEST")]
+ [DataRow("GET", "/api/test", "/api/test?id=1234")]
+ [DataRow("GET", "/api/test", "/api/test?id=")]
+ [DataRow("GET", "/api/test/resource/name", "/api/test/resource/name")]
+ [DataRow("GET", "/api/test/resource/name", "/api/test/resource/name?id=1234")]
+ [DataRow("GET", "/api/test/resource/name", "/api/test/resource/name?test=")]
+ [DataRow("GET", "/api/test/resource/name", "/api/test/resource/name?")]
+ [DataRow("GET", "/api/test/resource/name", "/api/test/resource/name?test=&id=123&app=something")]
+ public void IsRouteMatch_Should_ReturnTrueForMatchingMethodAndRoute(string method, string url, string invokedUrl)
+ {
+ Console.WriteLine(invokedUrl);
+ // Arrange
+ var route = new CallbackRoutes()
+ {
+ Method = method,
+ Route = url,
+ CaseSensitive = false
+ };
+
+ // Act
+ var result = WebServer.IsRouteMatch(route, method, invokedUrl);
+
+ // Assert
+ Assert.IsTrue(result);
+ }
+
+ // TODO: Case sensitive tests
+ }
+}
diff --git a/tests/nanoFramework.WebServer.Tests/nano.runsettings b/tests/nanoFramework.WebServer.Tests/nano.runsettings
new file mode 100644
index 0000000..93ce85e
--- /dev/null
+++ b/tests/nanoFramework.WebServer.Tests/nano.runsettings
@@ -0,0 +1,17 @@
+
+
+
+
+ .\TestResults
+ 120000
+ net48
+ x64
+
+
+ None
+ False
+ COM3
+
+
+
+
\ No newline at end of file
diff --git a/tests/nanoFramework.WebServer.Tests/nanoFramework.WebServer.Tests.nfproj b/tests/nanoFramework.WebServer.Tests/nanoFramework.WebServer.Tests.nfproj
new file mode 100644
index 0000000..2fecdae
--- /dev/null
+++ b/tests/nanoFramework.WebServer.Tests/nanoFramework.WebServer.Tests.nfproj
@@ -0,0 +1,73 @@
+
+
+
+ $(MSBuildExtensionsPath)\nanoFramework\v1.0\
+
+
+
+
+
+
+ Debug
+ AnyCPU
+ {11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 2c2b4750-2a48-4d19-9404-178aab946482
+ Library
+ Properties
+ 512
+ nanoFramework.WebServer.Tests
+ NFUnitTest
+ False
+ true
+ UnitTest
+ v1.0
+
+
+
+ $(MSBuildProjectDirectory)\nano.runsettings
+
+
+
+
+
+
+
+ ..\..\packages\nanoFramework.CoreLibrary.1.15.5\lib\mscorlib.dll
+
+
+ ..\..\packages\nanoFramework.Runtime.Events.1.11.15\lib\nanoFramework.Runtime.Events.dll
+
+
+ ..\..\packages\nanoFramework.System.Collections.1.5.31\lib\nanoFramework.System.Collections.dll
+
+
+ ..\..\packages\nanoFramework.System.Text.1.2.54\lib\nanoFramework.System.Text.dll
+
+
+ ..\..\packages\nanoFramework.TestFramework.2.1.87\lib\nanoFramework.TestFramework.dll
+
+
+ ..\..\packages\nanoFramework.TestFramework.2.1.87\lib\nanoFramework.UnitTestLauncher.exe
+
+
+ ..\..\packages\nanoFramework.System.IO.Streams.1.1.52\lib\System.IO.Streams.dll
+
+
+ ..\..\packages\nanoFramework.System.Net.1.10.68\lib\System.Net.dll
+
+
+ ..\..\packages\nanoFramework.System.Net.Http.Server.1.5.113\lib\System.Net.Http.dll
+
+
+ ..\..\packages\nanoFramework.System.Threading.1.1.32\lib\System.Threading.dll
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/nanoFramework.WebServer.Tests/packages.config b/tests/nanoFramework.WebServer.Tests/packages.config
new file mode 100644
index 0000000..12270f3
--- /dev/null
+++ b/tests/nanoFramework.WebServer.Tests/packages.config
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/nanoFramework.WebServer.Tests/packages.lock.json b/tests/nanoFramework.WebServer.Tests/packages.lock.json
new file mode 100644
index 0000000..a021e01
--- /dev/null
+++ b/tests/nanoFramework.WebServer.Tests/packages.lock.json
@@ -0,0 +1,61 @@
+{
+ "version": 1,
+ "dependencies": {
+ ".NETnanoFramework,Version=v1.0": {
+ "nanoFramework.CoreLibrary": {
+ "type": "Direct",
+ "requested": "[1.15.5, 1.15.5]",
+ "resolved": "1.15.5",
+ "contentHash": "u2+GvAp1uxLrGdILACAZy+EVKOs28EQ52j8Lz7599egXZ3GBGejjnR2ofhjMQwzrJLlgtyrsx8nSLngDfJNsAg=="
+ },
+ "nanoFramework.Runtime.Events": {
+ "type": "Direct",
+ "requested": "[1.11.15, 1.11.15]",
+ "resolved": "1.11.15",
+ "contentHash": "3uDNSTfiaewDAyi6fOMWYru0JCn/gr8DEv+Ro/V12SzojU9Dyxl5nSVOBtBXts7vErfIthB6SPiK180AMnrI8A=="
+ },
+ "nanoFramework.System.Collections": {
+ "type": "Direct",
+ "requested": "[1.5.31, 1.5.31]",
+ "resolved": "1.5.31",
+ "contentHash": "q7G0BHkbhUzpUJiOQNlZZDSMcZEU2/QJBDiSEQAF23wOya4EBaCXS74jAVcEfkHBgOkF413jKZq5vldpjqUfUw=="
+ },
+ "nanoFramework.System.IO.Streams": {
+ "type": "Direct",
+ "requested": "[1.1.52, 1.1.52]",
+ "resolved": "1.1.52",
+ "contentHash": "gdExWfWNSl4dgaIoVHHFmhLiRSKAabHA8ueHuErGAWd97qaoN2wSHCtvKqfOu1zuzyccbFpm4HBxVsh6bWMyXw=="
+ },
+ "nanoFramework.System.Net": {
+ "type": "Direct",
+ "requested": "[1.10.68, 1.10.68]",
+ "resolved": "1.10.68",
+ "contentHash": "PoM4NadhtMBnuGiOPqi+TJZD8xicz0v72hG5gPGDvWXeKDCMYCMqiCqY+di2pRL3qGHkF+hh8kLOf+wBUJBWNQ=="
+ },
+ "nanoFramework.System.Net.Http.Server": {
+ "type": "Direct",
+ "requested": "[1.5.113, 1.5.113]",
+ "resolved": "1.5.113",
+ "contentHash": "gaCPn1h0VGWRA5gG6FoWlm8W9PgoBMekOsgOKKJEhl90TEMJSI8VM/4Xlgzg8KY6tEKvUkn9uXBoVHa0f2vGLg=="
+ },
+ "nanoFramework.System.Text": {
+ "type": "Direct",
+ "requested": "[1.2.54, 1.2.54]",
+ "resolved": "1.2.54",
+ "contentHash": "k3OutSNRMs9di42LQ+5GbpHBY07aMEZWGkaS3Mj3ZU4cWqJc4deFGzRd+LBFQl1mRGdQaM5sl/euTZdcg8R9Zg=="
+ },
+ "nanoFramework.System.Threading": {
+ "type": "Direct",
+ "requested": "[1.1.32, 1.1.32]",
+ "resolved": "1.1.32",
+ "contentHash": "6o7Y4gH15FLuo2FWGLecABiCD57V5QMf5g/hEneV64VmhoXI8Bk7r6BDBPTfAePs738xbc1ECpA5dJmbSmtilg=="
+ },
+ "nanoFramework.TestFramework": {
+ "type": "Direct",
+ "requested": "[2.1.87, 2.1.87]",
+ "resolved": "2.1.87",
+ "contentHash": "58nW0UByML7fciTfvLfaEwysf6319SQWmah9v6N3xF9E7jWiHnh3FfEN/wtP/E2wwufmiFZAO2CidbiuEEJmKA=="
+ }
+ }
+ }
+}
\ No newline at end of file