@@ -43,7 +43,7 @@ public class WebServer : IDisposable
4343
4444 #region internal objects
4545
46- private bool _cancel = false ;
46+ private bool _cancel = true ;
4747 private Thread _serverThread = null ;
4848 private readonly ArrayList _callbackRoutes ;
4949 private readonly HttpListener _listener ;
@@ -92,6 +92,11 @@ public SslProtocols SslProtocols
9292 /// </summary>
9393 public string ApiKey { get ; set ; }
9494
95+ /// <summary>
96+ /// Gets a value indicating whether the web server is running.
97+ /// </summary>
98+ public bool IsRunning => ! _cancel ;
99+
95100 #endregion
96101
97102 #region Param
@@ -191,7 +196,7 @@ private void RegisterControllers(Type[] controllers)
191196 {
192197 return ;
193198 }
194-
199+
195200 foreach ( var controller in controllers )
196201 {
197202 var controlAttribs = controller . GetCustomAttributes ( true ) ;
@@ -216,7 +221,7 @@ private void RegisterControllers(Type[] controllers)
216221 {
217222 continue ;
218223 }
219-
224+
220225 var callbackRoutes = new CallbackRoutes
221226 {
222227 Route = ( ( RouteAttribute ) attrib ) . Route ,
@@ -330,6 +335,8 @@ private Authentication ExtractAuthentication(string strAuth)
330335 /// <summary>
331336 /// Delegate for the CommandReceived event.
332337 /// </summary>
338+ /// <param name="obj">The source of the event.</param>
339+ /// <param name="e">A WebServerEventArgs that contains the event data.</param>
333340 public delegate void GetRequestHandler ( object obj , WebServerEventArgs e ) ;
334341
335342 /// <summary>
@@ -338,6 +345,18 @@ private Authentication ExtractAuthentication(string strAuth)
338345 /// </summary>
339346 public event GetRequestHandler CommandReceived ;
340347
348+ /// <summary>
349+ /// Represents the method that will handle the WebServerStatusChanged event of a WebServer.
350+ /// </summary>
351+ /// <param name="obj">The source of the event.</param>
352+ /// <param name="e">A WebServerStatusEventArgs that contains the event data.</param>
353+ public delegate void WebServerStatusHandler ( object obj , WebServerStatusEventArgs e ) ;
354+
355+ /// <summary>
356+ /// Occurs when the status of the WebServer changes.
357+ /// </summary>
358+ public event WebServerStatusHandler WebServerStatusChanged ;
359+
341360 #endregion
342361
343362 #region Public and private methods
@@ -353,8 +372,10 @@ public bool Start()
353372 }
354373
355374 bool bStarted = true ;
375+ #if DEBUG
356376 // List Ethernet interfaces, so we can determine the server's address
357377 ListInterfaces ( ) ;
378+ #endif
358379 // start server
359380 try
360381 {
@@ -368,6 +389,12 @@ public bool Start()
368389 _cancel = true ;
369390 bStarted = false ;
370391 }
392+
393+ if ( bStarted )
394+ {
395+ WebServerStatusChanged ? . Invoke ( this , new WebServerStatusEventArgs ( WebServerStatus . Running ) ) ;
396+ }
397+
371398 return bStarted ;
372399 }
373400
@@ -380,7 +407,8 @@ public void Stop()
380407 Thread . Sleep ( 100 ) ;
381408 _serverThread . Abort ( ) ;
382409 _serverThread = null ;
383- Debug . WriteLine ( "Stopped server in thread " ) ;
410+ // Event is generate in the running thread
411+ Debug . WriteLine ( "Stopped server in thread " ) ;
384412 }
385413
386414 /// <summary>
@@ -487,106 +515,117 @@ public static void SendFileOverHTTP(HttpListenerResponse response, string fileNa
487515
488516 private void StartListener ( )
489517 {
490- _listener . Start ( ) ;
491- while ( ! _cancel )
518+ try
492519 {
493- HttpListenerContext context = _listener . GetContext ( ) ;
494- if ( context == null )
495- {
496- return ;
497- }
498-
499- new Thread ( ( ) =>
520+ _listener . Start ( ) ;
521+ while ( ! _cancel )
500522 {
501- //This is for handling with transitory or bad requests
502- if ( context . Request . RawUrl == null )
523+ HttpListenerContext context = _listener . GetContext ( ) ;
524+ if ( context == null )
503525 {
504526 return ;
505527 }
506528
507- // Variables used only within the "for". They are here for performance reasons
508- bool mustAuthenticate ;
509- bool isAuthOk ;
510- bool isRoute = false ;
511-
512- foreach ( CallbackRoutes route in _callbackRoutes )
529+ new Thread ( ( ) =>
513530 {
514- if ( ! IsRouteMatch ( route , context . Request . HttpMethod , context . Request . RawUrl ) )
531+ //This is for handling with transitory or bad requests
532+ if ( context . Request . RawUrl == null )
515533 {
516- continue ;
534+ return ;
517535 }
518536
519- isRoute = true ;
520-
521- // Check auth first
522- mustAuthenticate = route . Authentication != null && route . Authentication . AuthenticationType != AuthenticationType . None ;
523- isAuthOk = ! mustAuthenticate ;
537+ // Variables used only within the "for". They are here for performance reasons
538+ bool mustAuthenticate ;
539+ bool isAuthOk ;
540+ bool isRoute = false ;
524541
525- if ( mustAuthenticate )
542+ foreach ( CallbackRoutes route in _callbackRoutes )
526543 {
527- if ( route . Authentication . AuthenticationType == AuthenticationType . Basic )
544+ if ( ! IsRouteMatch ( route , context . Request . HttpMethod , context . Request . RawUrl ) )
528545 {
529- var credSite = route . Authentication . Credentials ?? Credential ;
530- var credReq = context . Request . Credentials ;
531-
532- isAuthOk = credReq != null
533- && ( credSite . UserName == credReq . UserName )
534- && ( credSite . Password == credReq . Password ) ;
546+ continue ;
535547 }
536- else if ( route . Authentication . AuthenticationType == AuthenticationType . ApiKey )
537- {
538- var apikeySite = route . Authentication . ApiKey ?? ApiKey ;
539- var apikeyReq = GetApiKeyFromHeaders ( context . Request . Headers ) ;
540548
541- isAuthOk = apikeyReq != null
542- && apikeyReq == apikeySite ;
543- }
544- }
549+ isRoute = true ;
545550
546- if ( ! isAuthOk )
547- {
548- if ( route . Authentication != null &&
549- route . Authentication . AuthenticationType == AuthenticationType . Basic )
551+ // Check auth first
552+ mustAuthenticate = route . Authentication != null && route . Authentication . AuthenticationType != AuthenticationType . None ;
553+ isAuthOk = ! mustAuthenticate ;
554+
555+ if ( mustAuthenticate )
550556 {
551- context . Response . Headers . Add ( "WWW-Authenticate" ,
552- $ "Basic realm=\" Access to { route . Route } \" ") ;
557+ if ( route . Authentication . AuthenticationType == AuthenticationType . Basic )
558+ {
559+ var credSite = route . Authentication . Credentials ?? Credential ;
560+ var credReq = context . Request . Credentials ;
561+
562+ isAuthOk = credReq != null
563+ && ( credSite . UserName == credReq . UserName )
564+ && ( credSite . Password == credReq . Password ) ;
565+ }
566+ else if ( route . Authentication . AuthenticationType == AuthenticationType . ApiKey )
567+ {
568+ var apikeySite = route . Authentication . ApiKey ?? ApiKey ;
569+ var apikeyReq = GetApiKeyFromHeaders ( context . Request . Headers ) ;
570+
571+ isAuthOk = apikeyReq != null
572+ && apikeyReq == apikeySite ;
573+ }
553574 }
554575
555- context . Response . StatusCode = ( int ) HttpStatusCode . Unauthorized ;
556- context . Response . ContentLength64 = 0 ;
576+ if ( ! isAuthOk )
577+ {
578+ if ( route . Authentication != null &&
579+ route . Authentication . AuthenticationType == AuthenticationType . Basic )
580+ {
581+ context . Response . Headers . Add ( "WWW-Authenticate" ,
582+ $ "Basic realm=\" Access to { route . Route } \" ") ;
583+ }
584+
585+ context . Response . StatusCode = ( int ) HttpStatusCode . Unauthorized ;
586+ context . Response . ContentLength64 = 0 ;
587+
588+ HandleContextResponse ( context ) ;
589+ return ;
590+ }
557591
592+ InvokeRoute ( route , context ) ;
558593 HandleContextResponse ( context ) ;
559- return ;
560594 }
561595
562- InvokeRoute ( route , context ) ;
563- HandleContextResponse ( context ) ;
564- }
565-
566- if ( ! isRoute )
567- {
568- if ( CommandReceived != null )
596+ if ( ! isRoute )
569597 {
570- // Starting a new thread to be able to handle a new request in parallel
571- CommandReceived . Invoke ( this , new WebServerEventArgs ( context ) ) ;
572- }
573- else
574- {
575- context . Response . StatusCode = ( int ) HttpStatusCode . NotFound ;
576- context . Response . ContentLength64 = 0 ;
598+ if ( CommandReceived != null )
599+ {
600+ // Starting a new thread to be able to handle a new request in parallel
601+ CommandReceived . Invoke ( this , new WebServerEventArgs ( context ) ) ;
602+ }
603+ else
604+ {
605+ context . Response . StatusCode = ( int ) HttpStatusCode . NotFound ;
606+ context . Response . ContentLength64 = 0 ;
607+ }
608+
609+ HandleContextResponse ( context ) ;
577610 }
611+ } ) . Start ( ) ;
578612
579- HandleContextResponse ( context ) ;
580- }
581- } ) . Start ( ) ;
613+ }
582614
615+ if ( _listener . IsListening )
616+ {
617+ _listener . Stop ( ) ;
618+ }
583619 }
584- if ( _listener . IsListening )
620+ catch
585621 {
586- _listener . Stop ( ) ;
587- }
622+ // If we are here then set the server state to not running
623+ _cancel = true ;
624+ }
625+
626+ WebServerStatusChanged ? . Invoke ( this , new WebServerStatusEventArgs ( WebServerStatus . Stopped ) ) ;
588627 }
589-
628+
590629 /// <summary>
591630 /// Checks if route matches called resource.
592631 /// For internal use only.
@@ -601,14 +640,14 @@ public static bool IsRouteMatch(CallbackRoutes route, string method, string rawU
601640 {
602641 return false ;
603642 }
604-
643+
605644 var urlParam = rawUrl . IndexOf ( ParamStart ) ;
606645 var incForSlash = route . Route . IndexOf ( '/' ) == 0 ? 0 : 1 ;
607646 var rawUrlToCompare = route . CaseSensitive ? rawUrl : rawUrl . ToLower ( ) ;
608647 var routeToCompare = route . CaseSensitive ? route . Route : route . Route . ToLower ( ) ;
609648 bool isFound ;
610-
611- if ( urlParam > 0 )
649+
650+ if ( urlParam > 0 )
612651 {
613652 isFound = urlParam == routeToCompare . Length + incForSlash ;
614653 }
@@ -771,6 +810,6 @@ protected virtual void Dispose(bool disposing)
771810 }
772811 }
773812
774- #endregion
813+ #endregion
775814 }
776815}
0 commit comments