Ignore:
Timestamp:
Aug 1, 2022, 12:54:31 PM (2 years ago)
Author:
alloc
Message:

Switched to use SpaceWizards.HttpListener

File:
1 edited

Legend:

Unmodified
Added
Removed
  • binary-improvements2/MapRendering/Web/Web.cs

    r369 r382  
    22using System.Collections.Generic;
    33using System.IO;
    4 using System.Net;
    54using System.Net.Sockets;
    6 using System.Reflection;
     5using Cookie = System.Net.Cookie;
     6using HttpStatusCode = System.Net.HttpStatusCode;
     7using IPEndPoint = System.Net.IPEndPoint;
    78using System.Text;
    89using System.Threading;
     
    1011using AllocsFixes.NetConnections.Servers.Web.Handlers;
    1112using AllocsFixes.NetConnections.Servers.Web.SSE;
     13using SpaceWizards.HttpListener;
    1214using UnityEngine;
    1315
    1416namespace AllocsFixes.NetConnections.Servers.Web {
    1517        public class Web : IConsoleServer {
    16                 private const int GUEST_PERMISSION_LEVEL = 2000;
     18                private const int guestPermissionLevel = 2000;
     19                private const string indexPagePath = "/app";
     20               
    1721                public static int handlingCount;
    1822                public static int currentHandlers;
    1923                public static long totalHandlingTime = 0;
     24                private readonly List<AbsHandler> handlers = new List<AbsHandler> ();
     25                private readonly ConnectionHandler connectionHandler;
     26
    2027                private readonly HttpListener listener = new HttpListener ();
    21                 private readonly Dictionary<string, PathHandler> handlers = new CaseInsensitiveStringDictionary<PathHandler> ();
    22 
    23                 public readonly ConnectionHandler connectionHandler;
    24 
    25                 public Web () {
     28                private readonly Version httpProtocolVersion = new Version(1, 1);
     29
     30                public Web (string _modInstancePath) {
    2631                        try {
    2732                                int webPort = GamePrefs.GetInt (EnumUtils.Parse<EnumGamePrefs> ("ControlPanelPort"));
     
    3136                                }
    3237
    33                                 if (!Directory.Exists (Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) +
    34                                                        "/webserver")) {
    35                                         Log.Out ("Webserver not started (folder \"webserver\" not found in WebInterface mod folder)");
     38                                // TODO: Remove once this becomes the default control panel
     39                                webPort += 2;
     40
     41                                if (!HttpListener.IsSupported) {
     42                                        Log.Out ("Webserver not started (needs Windows XP SP2, Server 2003 or later or Mono)");
    3643                                        return;
    3744                                }
     
    4047                                bool useStaticCache = false;
    4148
    42                                 string dataFolder = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) + "/webserver";
    43 
    44                                 if (!HttpListener.IsSupported) {
    45                                         Log.Out ("Webserver not started (needs Windows XP SP2, Server 2003 or later or Mono)");
    46                                         return;
    47                                 }
    48 
    49                                
    50                                 RegisterPathHandler ("/index.htm", new SimpleRedirectHandler ("/static/index.html"));
    51                                 RegisterPathHandler ("/favicon.ico", new SimpleRedirectHandler ("/static/favicon.ico"));
    52                                 RegisterPathHandler ("/session/", new SessionHandler (dataFolder));
     49                                string webfilesFolder = _modInstancePath + "/webserver";
     50                                string webfilesFolderLegacy = _modInstancePath + "/weblegacy";
     51
     52                                connectionHandler = new ConnectionHandler ();
     53                               
     54                                RegisterPathHandler ("/", new RewriteHandler ("/files/"));
     55
     56                                // React virtual routing
     57                                RegisterPathHandler ("/app", new RewriteHandler ("/files/index.html", true));
     58                               
     59                                // Legacy web page
     60                                RegisterPathHandler ("/weblegacy", new StaticHandler (
     61                                        webfilesFolderLegacy,
     62                                        useStaticCache ? (AbstractCache)new SimpleCache () : new DirectAccess (),
     63                                        false)
     64                                );
     65                               
     66                                RegisterPathHandler ("/session/", new SessionHandler (webfilesFolder, connectionHandler));
    5367                                RegisterPathHandler ("/userstatus", new UserStatusHandler ());
    54                                 RegisterPathHandler ("/static/", new StaticHandler (
    55                                                 dataFolder,
     68                                RegisterPathHandler ("/files/", new StaticHandler (
     69                                                webfilesFolder,
    5670                                                useStaticCache ? (AbstractCache) new SimpleCache () : new DirectAccess (),
    5771                                                false)
     
    6781                                RegisterPathHandler ("/sse/", new SseHandler ());
    6882
    69                                 connectionHandler = new ConnectionHandler ();
    70 
    71                                 listener.Prefixes.Add ($"http://*:{webPort + 2}/");
     83                                listener.Prefixes.Add ($"http://+:{webPort}/");
     84                                // listener.Prefixes.Add ($"http://[::1]:{webPort}/");
    7285                                listener.Start ();
     86                                listener.BeginGetContext (HandleRequest, listener);
    7387
    7488                                SdtdConsole.Instance.RegisterServer (this);
    7589
    76                                 listener.BeginGetContext (HandleRequest, listener);
    77 
    78                                 Log.Out ("Started Webserver on " + (webPort + 2));
     90                                Log.Out ("Started Webserver on " + webPort);
    7991                        } catch (Exception e) {
    80                                 Log.Out ("Error in Web.ctor: " + e);
    81                         }
    82                 }
    83 
    84                 public void RegisterPathHandler (string _urlBasePath, PathHandler _handler) {
    85                         if (handlers.ContainsKey (_urlBasePath)) {
    86                                 Log.Error ($"Web: Handler for relative path {_urlBasePath} already registerd.");
    87                                 return;
     92                                Log.Error ("Error in Web.ctor: ");
     93                                Log.Exception (e);
     94                        }
     95                }
     96
     97                public void RegisterPathHandler (string _urlBasePath, AbsHandler _handler) {
     98                        foreach (AbsHandler handler in handlers) {
     99                                if (handler.UrlBasePath == _urlBasePath) {
     100                                        Log.Error ($"Web: Handler for relative path {_urlBasePath} already registerd.");
     101                                        return;
     102                                }
    88103                        }
    89104                       
    90                         handlers.Add (_urlBasePath, _handler);
     105                        handlers.Add (_handler);
    91106                        _handler.SetBasePathAndParent (this, _urlBasePath);
    92107                }
     
    102117
    103118                public void Shutdown () {
    104                         foreach (KeyValuePair<string, PathHandler> kvp in handlers) {
    105                                 kvp.Value.Shutdown ();
     119                        foreach (AbsHandler handler in handlers) {
     120                                handler.Shutdown ();
    106121                        }
    107122                }
     
    120135                }
    121136               
    122                 private readonly Version HttpProtocolVersion = new Version(1, 1);
    123                
    124137#if ENABLE_PROFILER
    125138                private readonly UnityEngine.Profiling.CustomSampler authSampler = UnityEngine.Profiling.CustomSampler.Create ("Auth");
     
    128141
    129142                private void HandleRequest (IAsyncResult _result) {
    130                         if (!listener.IsListening) {
     143                        HttpListener listenerInstance = (HttpListener)_result.AsyncState;
     144                        if (!listenerInstance.IsListening) {
    131145                                return;
    132146                        }
     
    135149                        Interlocked.Increment (ref currentHandlers);
    136150
    137 //                              MicroStopwatch msw = new MicroStopwatch ();
    138151#if ENABLE_PROFILER
    139152                        UnityEngine.Profiling.Profiler.BeginThreadProfiling ("AllocsMods", "WebRequest");
    140                         HttpListenerContext ctx = _listener.EndGetContext (_result);
     153                        HttpListenerContext ctx = listenerInstance.EndGetContext (_result);
    141154                        try {
    142155#else
    143                         HttpListenerContext ctx = listener.EndGetContext (_result);
    144                         listener.BeginGetContext (HandleRequest, listener);
     156                        HttpListenerContext ctx = listenerInstance.EndGetContext (_result);
     157                        listenerInstance.BeginGetContext (HandleRequest, listenerInstance);
    145158#endif
    146159                        try {
     
    149162                                response.SendChunked = false;
    150163
    151                                 response.ProtocolVersion = HttpProtocolVersion;
     164                                response.ProtocolVersion = httpProtocolVersion;
    152165
    153166#if ENABLE_PROFILER
     
    179192                                }
    180193
    181                                 if (request.Url.AbsolutePath.Length < 2) {
    182                                         handlers ["/index.htm"].HandleRequest (request, response, conn, permissionLevel);
    183                                         return;
    184                                 } else {
    185                                         foreach (KeyValuePair<string, PathHandler> kvp in handlers) {
    186                                                 if (request.Url.AbsolutePath.StartsWith (kvp.Key)) {
    187                                                         if (!kvp.Value.IsAuthorizedForHandler (conn, permissionLevel)) {
    188                                                                 response.StatusCode = (int) HttpStatusCode.Forbidden;
    189                                                                 if (conn != null) {
    190                                                                         //Log.Out ("Web.HandleRequest: user '{0}' not allowed to access '{1}'", conn.SteamID, kvp.Value.ModuleName);
    191                                                                 }
    192                                                         } else {
    193 #if ENABLE_PROFILER
    194                                                                 handlerSampler.Begin ();
    195 #endif
    196                                                                 kvp.Value.HandleRequest (request, response, conn, permissionLevel);
    197 #if ENABLE_PROFILER
    198                                                                 handlerSampler.End ();
    199 #endif
    200                                                         }
    201 
    202                                                         return;
    203                                                 }
    204                                         }
    205                                 }
    206 
    207                                 // Not really relevant for non-debugging purposes:
    208                                 //Log.Out ("Error in Web.HandleRequest(): No handler found for path \"" + request.Url.AbsolutePath + "\"");
    209                                 response.StatusCode = (int) HttpStatusCode.NotFound;
     194                                string requestPath = request.Url.AbsolutePath;
     195
     196                                if (requestPath.Length < 2) {
     197                                        response.Redirect (indexPagePath);
     198                                        return;
     199                                }
     200                               
     201                                ApplyPathHandler (requestPath, request, response, conn, permissionLevel);
     202
    210203                        } catch (IOException e) {
    211204                                if (e.InnerException is SocketException) {
    212                                         Log.Out ("Error in Web.HandleRequest(): Remote host closed connection: " +
    213                                                  e.InnerException.Message);
     205                                        Log.Out ("Error in Web.HandleRequest(): Remote host closed connection: " + e.InnerException.Message);
    214206                                } else {
    215207                                        Log.Out ("Error (IO) in Web.HandleRequest(): " + e);
     
    219211                                Log.Exception (e);
    220212                        } finally {
    221                                 if (ctx != null && !ctx.Response.SendChunked) {
     213                                if (!ctx.Response.SendChunked) {
    222214                                        ctx.Response.Close ();
    223215                                }
    224 
    225 //                                      msw.Stop ();
    226 //                                      totalHandlingTime += msw.ElapsedMicroseconds;
    227 //                                      Log.Out ("Web.HandleRequest(): Took {0} µs", msw.ElapsedMicroseconds);
    228216                                Interlocked.Decrement (ref currentHandlers);
    229217                        }
    230218#if ENABLE_PROFILER
    231219                        } finally {
    232                                 _listener.BeginGetContext (HandleRequest, _listener);
     220                                listenerInstance.BeginGetContext (HandleRequest, listenerInstance);
    233221                                UnityEngine.Profiling.Profiler.EndThreadProfiling ();
    234222                        }
    235223#endif
     224                }
     225
     226                public void ApplyPathHandler (string _requestPath, HttpListenerRequest _req, HttpListenerResponse _resp, WebConnection _con,
     227                        int _permissionLevel) {
     228                        for (int i = handlers.Count - 1; i >= 0; i--) {
     229                                AbsHandler handler = handlers [i];
     230                               
     231                                if (_requestPath.StartsWith (handler.UrlBasePath)) {
     232                                        if (!handler.IsAuthorizedForHandler (_con, _permissionLevel)) {
     233                                                _resp.StatusCode = (int)HttpStatusCode.Forbidden;
     234                                                if (_con != null) {
     235                                                        //Log.Out ("Web.HandleRequest: user '{0}' not allowed to access '{1}'", _con.SteamID, handler.ModuleName);
     236                                                }
     237                                        } else {
     238#if ENABLE_PROFILER
     239                                                handlerSampler.Begin ();
     240#endif
     241                                                handler.HandleRequest (_requestPath, _req, _resp, _con, _permissionLevel);
     242#if ENABLE_PROFILER
     243                                                handlerSampler.End ();
     244#endif
     245                                        }
     246
     247                                        return;
     248                                }
     249                        }
     250
     251                        // Not really relevant for non-debugging purposes:
     252                        //Log.Out ("Error in Web.HandleRequest(): No handler found for path \"" + _requestPath + "\"");
     253                        _resp.StatusCode = (int) HttpStatusCode.NotFound;
    236254                }
    237255
     
    239257                        _con = null;
    240258
    241                         string sessionId = null;
    242                         if (_req.Cookies ["sid"] != null) {
    243                                 sessionId = _req.Cookies ["sid"].Value;
    244                         }
    245 
     259                        string sessionId = _req.Cookies ["sid"]?.Value;
     260
     261                        IPEndPoint reqRemoteEndPoint = _req.RemoteEndPoint;
     262                        if (reqRemoteEndPoint == null) {
     263                                Log.Warning ("No RemoteEndPoint on web request");
     264                                return guestPermissionLevel;
     265                        }
     266                       
    246267                        if (!string.IsNullOrEmpty (sessionId)) {
    247                                 WebConnection con = connectionHandler.IsLoggedIn (sessionId, _req.RemoteEndPoint.Address);
    248                                 if (con != null) {
    249                                         _con = con;
     268                                _con = connectionHandler.IsLoggedIn (sessionId, reqRemoteEndPoint.Address);
     269                                if (_con != null) {
    250270                                        return GameManager.Instance.adminTools.GetUserPermissionLevel (_con.UserId);
    251271                                }
    252272                        }
    253273
    254                         string remoteEndpointString = _req.RemoteEndPoint.ToString ();
     274                        string remoteEndpointString = reqRemoteEndPoint.ToString ();
    255275
    256276                        if (_req.QueryString ["adminuser"] != null && _req.QueryString ["admintoken"] != null) {
     
    264284                        }
    265285
    266                         if (_req.Url.AbsolutePath.StartsWith ("/session/verify", StringComparison.OrdinalIgnoreCase)) {
    267                                 try {
    268                                         ulong id = OpenID.Validate (_req);
    269                                         if (id > 0) {
    270                                                 WebConnection con = connectionHandler.LogIn (id, _req.RemoteEndPoint.Address);
    271                                                 _con = con;
    272                                                 int level = GameManager.Instance.adminTools.GetUserPermissionLevel (con.UserId);
    273                                                 Log.Out ("Steam OpenID login from {0} with ID {1}, permission level {2}",
    274                                                         remoteEndpointString, con.UserId, level);
    275                                                 return level;
    276                                         }
    277 
    278                                         Log.Out ("Steam OpenID login failed from {0}", remoteEndpointString);
    279                                 } catch (Exception e) {
    280                                         Log.Error ("Error validating login:");
    281                                         Log.Exception (e);
    282                                 }
    283                         }
    284 
    285                         return GUEST_PERMISSION_LEVEL;
     286                        return guestPermissionLevel;
    286287                }
    287288
Note: See TracChangeset for help on using the changeset viewer.