source: binary-improvements/MapRendering/Web/Web.cs@ 287

Last change on this file since 287 was 286, checked in by alloc, 8 years ago

No more errors in log when requesting web stuff while server is still booting

File size: 8.3 KB
RevLine 
[230]1using System;
2using System.Collections.Generic;
[244]3using System.Collections.Specialized;
[230]4using System.IO;
5using System.Net;
6using System.Net.Sockets;
7using System.Reflection;
8using System.Text;
9using System.Threading;
10using UnityEngine;
11
[244]12using AllocsFixes.NetConnections.Servers.Web.Handlers;
13
[230]14namespace AllocsFixes.NetConnections.Servers.Web
15{
16 public class Web : IConsoleServer {
[244]17 private const int GUEST_PERMISSION_LEVEL = 2000;
[230]18 private readonly HttpListener _listener = new HttpListener ();
19 private Dictionary<string, PathHandler> handlers = new Dictionary<string, PathHandler> ();
20 public static int handlingCount = 0;
21 public static int currentHandlers = 0;
[279]22 public static long totalHandlingTime = 0;
[230]23 private string dataFolder;
[244]24 private bool useStaticCache = false;
[230]25
[244]26 public bool isSslRedirected {
27 private set;
28 get;
29 }
30
31 public ConnectionHandler connectionHandler;
32
[230]33 public Web () {
34 try {
35 int webPort = GamePrefs.GetInt (EnumGamePrefs.ControlPanelPort);
36 if (webPort < 1 || webPort > 65533) {
[244]37 Log.Out ("Webserver not started (ControlPanelPort not within 1-65533)");
[230]38 return;
39 }
40 if (!Directory.Exists (Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) + "/webserver")) {
41 Log.Out ("Webserver not started (folder \"webserver\" not found in WebInterface mod folder)");
42 return;
43 }
44
[244]45 // TODO: Read from config
46 isSslRedirected = false;
47 useStaticCache = false;
48
[230]49 dataFolder = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) + "/webserver";
50
51 if (!HttpListener.IsSupported) {
52 Log.Out ("Webserver not started (needs Windows XP SP2, Server 2003 or later or Mono)");
53 return;
54 }
55
56 handlers.Add (
57 "/index.htm",
58 new SimpleRedirectHandler ("/static/index.html"));
59 handlers.Add (
60 "/favicon.ico",
61 new SimpleRedirectHandler ("/static/favicon.ico"));
62 handlers.Add (
[244]63 "/session/",
64 new SessionHandler (
65 "/session/",
66 dataFolder,
67 this)
68 );
69 handlers.Add (
70 "/userstatus",
71 new UserStatusHandler ()
72 );
73 if (useStaticCache) {
74 handlers.Add (
75 "/static/",
76 new StaticHandler (
77 "/static/",
78 dataFolder,
79 new AllocsFixes.FileCache.SimpleCache (),
80 true)
81 );
82 } else {
83 handlers.Add (
84 "/static/",
85 new StaticHandler (
86 "/static/",
87 dataFolder,
88 new AllocsFixes.FileCache.DirectAccess (),
89 true)
90 );
91 }
[230]92
93 handlers.Add (
[238]94 "/itemicons/",
95 new ItemIconHandler (
96 "/itemicons/",
97 true)
98 );
99
100 handlers.Add (
[230]101 "/map/",
102 new StaticHandler (
103 "/map/",
[238]104 GameUtils.GetSaveGameDir () + "/map",
[230]105 MapRendering.MapRendering.GetTileCache (),
[244]106 false,
107 "web.map")
[230]108 );
109
[244]110 handlers.Add (
111 "/api/",
112 new ApiHandler ("/api/")
113 );
[230]114
[244]115 connectionHandler = new ConnectionHandler (this);
116
[230]117 _listener.Prefixes.Add (String.Format ("http://*:{0}/", webPort + 2));
118 _listener.Start ();
119
120 SdtdConsole.Instance.RegisterServer (this);
121
122 _listener.BeginGetContext (new AsyncCallback (HandleRequest), _listener);
123
[244]124 Log.Out ("Started Webserver on " + (webPort + 2));
[230]125 } catch (Exception e) {
126 Log.Out ("Error in Web.ctor: " + e);
127 }
128 }
129
130 private void HandleRequest (IAsyncResult result) {
131 if (_listener.IsListening) {
132 Interlocked.Increment (ref handlingCount);
133 Interlocked.Increment (ref currentHandlers);
[282]134// MicroStopwatch msw = new MicroStopwatch ();
[230]135 HttpListenerContext ctx = _listener.EndGetContext (result);
136 _listener.BeginGetContext (new AsyncCallback (HandleRequest), _listener);
137 try {
[244]138 HttpListenerRequest request = ctx.Request;
139 HttpListenerResponse response = ctx.Response;
[279]140 response.SendChunked = false;
[230]141
[244]142 response.ProtocolVersion = new Version ("1.1");
[230]143
[244]144 WebConnection conn;
145 int permissionLevel = DoAuthentication (request, out conn);
146
147
148 //Log.Out ("Login status: conn!=null: {0}, permissionlevel: {1}", conn != null, permissionLevel);
149
150
151 if (conn != null) {
152 Cookie cookie = new Cookie ("sid", conn.SessionID, "/");
153 cookie.Expired = false;
154 cookie.Expires = new DateTime (2020, 1, 1);
155 cookie.HttpOnly = true;
156 cookie.Secure = false;
157 response.AppendCookie (cookie);
158 }
159
[286]160 // No game yet -> fail request
161 if (GameManager.Instance.World == null) {
162 response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
163 return;
164 }
165
[244]166 if (request.Url.AbsolutePath.Length < 2) {
167 handlers ["/index.htm"].HandleRequest (request, response, conn, permissionLevel);
168 return;
169 } else {
170 foreach (KeyValuePair<string, PathHandler> kvp in handlers) {
171 if (request.Url.AbsolutePath.StartsWith (kvp.Key)) {
172 if (!kvp.Value.IsAuthorizedForHandler (conn, permissionLevel)) {
173 response.StatusCode = (int)HttpStatusCode.Forbidden;
174 if (conn != null) {
[247]175 //Log.Out ("Web.HandleRequest: user '{0}' not allowed to access '{1}'", conn.SteamID, kvp.Value.ModuleName);
[244]176 } else {
[247]177 //Log.Out ("Web.HandleRequest: unidentified user from '{0}' not allowed to access '{1}'", request.RemoteEndPoint.Address, kvp.Value.ModuleName);
[244]178 }
179 } else {
180 kvp.Value.HandleRequest (request, response, conn, permissionLevel);
[230]181 }
[244]182 return;
[230]183 }
184 }
[244]185 }
[230]186
[244]187 Log.Out ("Error in Web.HandleRequest(): No handler found for path \"" + request.Url.AbsolutePath + "\"");
188 response.StatusCode = (int)HttpStatusCode.NotFound;
[230]189 } catch (IOException e) {
190 if (e.InnerException is SocketException) {
191 Log.Out ("Error in Web.HandleRequest(): Remote host closed connection: " + e.InnerException.Message);
192 } else {
193 Log.Out ("Error (IO) in Web.HandleRequest(): " + e);
194 }
195 } catch (Exception e) {
196 Log.Out ("Error in Web.HandleRequest(): " + e);
197 } finally {
[279]198 if (ctx != null && !ctx.Response.SendChunked) {
[244]199 ctx.Response.Close ();
[230]200 }
[282]201// msw.Stop ();
202// totalHandlingTime += msw.ElapsedMicroseconds;
203// Log.Out ("Web.HandleRequest(): Took {0} µs", msw.ElapsedMicroseconds);
[230]204 Interlocked.Decrement (ref currentHandlers);
205 }
206 }
207 }
208
[244]209 private int DoAuthentication (HttpListenerRequest _req, out WebConnection _con) {
210 _con = null;
211
212 string sessionId = null;
213 if (_req.Cookies ["sid"] != null) {
214 sessionId = _req.Cookies ["sid"].Value;
[230]215 }
[244]216
217 if (!string.IsNullOrEmpty (sessionId)) {
218 WebConnection con = connectionHandler.IsLoggedIn (sessionId, _req.RemoteEndPoint.Address.ToString ());
219 if (con != null) {
220 _con = con;
221 return GameManager.Instance.adminTools.GetAdminToolsClientInfo (_con.SteamID.ToString ()).PermissionLevel;
222 }
223 }
224
225 if (_req.QueryString ["adminuser"] != null && _req.QueryString ["admintoken"] != null) {
226 WebPermissions.AdminToken admin = WebPermissions.Instance.GetWebAdmin (_req.QueryString ["adminuser"], _req.QueryString ["admintoken"]);
227 if (admin != null) {
228 return admin.permissionLevel;
229 } else {
230 Log.Warning ("Invalid Admintoken used from " + _req.RemoteEndPoint.ToString ());
231 }
232 }
233
234 if (_req.Url.AbsolutePath.StartsWith ("/session/verify")) {
235 ulong id = OpenID.Validate (_req);
236 if (id > 0) {
237 WebConnection con = connectionHandler.LogIn (id, _req.RemoteEndPoint.Address.ToString ());
238 _con = con;
[249]239 int level = GameManager.Instance.adminTools.GetAdminToolsClientInfo (id.ToString ()).PermissionLevel;
240 Log.Out ("Steam OpenID login from {0} with ID {1}, permission level {2}", _req.RemoteEndPoint.ToString (), con.SteamID, level);
241 return level;
[244]242 } else {
243 Log.Out ("Steam OpenID login failed from {0}", _req.RemoteEndPoint.ToString ());
244 }
245 }
246
247 return GUEST_PERMISSION_LEVEL;
[230]248 }
249
250 public void Disconnect () {
251 try {
252 _listener.Stop ();
253 _listener.Close ();
254 } catch (Exception e) {
255 Log.Out ("Error in Web.Disconnect: " + e);
256 }
257 }
258
259 public void SendLine (string line) {
[244]260 connectionHandler.SendLine (line);
[230]261 }
262
263 public void SendLog (string text, string trace, UnityEngine.LogType type) {
[250]264 // Do nothing, handled by LogBuffer internally
[230]265 }
266
267 public static void SetResponseTextContent (HttpListenerResponse resp, string text) {
268 byte[] buf = Encoding.UTF8.GetBytes (text);
269 resp.ContentLength64 = buf.Length;
270 resp.ContentType = "text/html";
271 resp.ContentEncoding = Encoding.UTF8;
272 resp.OutputStream.Write (buf, 0, buf.Length);
273 }
274
275 }
276}
Note: See TracBrowser for help on using the repository browser.