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

Last change on this file since 298 was 286, checked in by alloc, 10 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.