public void ProcessRequest(HttpApplication webapp) { webapp.HttpResponse.Server = "Microsoft .Net Terrarium"; webapp.HttpResponse.Date = DateTime.Now; webapp.HttpResponse.ContentType = "text/xml"; webapp.HttpResponse.StatusCode = HttpStatusCode.OK; webapp.HttpResponse.StatusDescription = "OK"; webapp.HttpResponse.KeepAlive = false; string body; if (webapp.HttpRequest.Method == "GET") { body = "<version><build>" + peerVersion.Build + "</build>"; body += "<major>" + peerVersion.Major + "</major>\t"; body += "<minor>" + peerVersion.Minor + "</minor>\t</version>"; body += "<channel>" + GameEngine.Current.PeerChannel + "</channel>"; } else { webapp.HttpResponse.StatusCode = HttpStatusCode.MethodNotAllowed; webapp.HttpResponse.StatusDescription = "Method Not Allowed"; body = "<HTML><BODY>" + "The method " + webapp.HttpRequest.Method; body += " is not allowed.</BODY></HTML>"; webapp.HttpResponse.ContentType = "text/html"; } // Encode the body message and place it on the stream. var bodyBytes = Encoding.ASCII.GetBytes(body); webapp.HttpResponse.ContentLength = bodyBytes.Length; webapp.HttpResponse.Close(bodyBytes); }
/// <summary> /// Start listening on the given host IP Address, on the given /// port. /// </summary> /// <param name="hostIP">The host's IP address to bind to.</param> /// <param name="port">The port to bind on.</param> public void Start(string hostIP, int port) { HttpTraceHelper.ExceptionCaught.Level = TraceLevel.Verbose; _listener = new HttpWebListener(); _listener.Start(port); // Put this in a loop if we want more listeners in the pool var stateObject = new HttpApplication(); _listener.BeginGetRequest(_getRequestCB, stateObject); }
/// <summary> /// Used to handle a request where the method is supported /// but insufficient data is available. The primary culprit /// here is a POST operation with no POST data. /// </summary> /// <param name="state">The web application state object for this request.</param> /// <param name="message">The message to be sent to the client to describe the failure.</param> public void HandleBadRequest(HttpApplication state, string message) { state.HttpResponse.StatusCode = HttpStatusCode.BadRequest; state.HttpResponse.StatusDescription = "Bad Request"; state.HttpResponse.Server = "Microsoft .Net Terrarium"; state.HttpResponse.Date = DateTime.Now; state.HttpResponse.ContentType = "text/html"; state.HttpResponse.KeepAlive = false; var body = message; var bodyBytes = Encoding.ASCII.GetBytes(body); state.HttpResponse.ContentLength = bodyBytes.Length; state.HttpResponse.Close(bodyBytes); }
/// <summary> /// Handles a namespace that isn't currently bound by this namespace /// manager. This could happen because of case sensitivity issues, trailing /// path characters or any other string morph that causes the namespace /// to not match one that is currently registered. This should only happen /// through a failed user connection using a web browser and a mistyped path. /// </summary> /// <param name="state">The web application state for the current connection.</param> public void HandleUnsupportedNamespace(HttpApplication state) { state.HttpResponse.StatusCode = HttpStatusCode.OK; state.HttpResponse.StatusDescription = "OK"; state.HttpResponse.Server = "Microsoft .Net Terrarium"; state.HttpResponse.Date = DateTime.Now; state.HttpResponse.ContentType = "text/html"; state.HttpResponse.KeepAlive = false; var body = "<HTML><BODY>Unsupported URI.</BODY></HTML>"; var bodyBytes = Encoding.ASCII.GetBytes(body); state.HttpResponse.ContentLength = bodyBytes.Length; state.HttpResponse.Close(bodyBytes); }
/// <summary> /// Handles a method that is not supported by the current /// namespace implementation. This happens whenever a users /// requests a namespace using a method that isn't valid. This /// should only happen if Terrarium's of differing versions /// connect to one another, or someone hits the peer with /// a web browser and changes the Method to something other than /// GET or POST. /// </summary> /// <param name="state">The web server state object for the connection.</param> public void HandleUnsupportedMethod(HttpApplication state) { state.HttpResponse.StatusCode = HttpStatusCode.MethodNotAllowed; state.HttpResponse.StatusDescription = "Method Not Allowed"; state.HttpResponse.Server = "Microsoft .Net Terrarium"; state.HttpResponse.Date = DateTime.Now; state.HttpResponse.ContentType = "text/html"; state.HttpResponse.KeepAlive = false; var body = "<HTML><BODY>" + "The method " + state.HttpRequest.Method; body += " is not allowed.</BODY></HTML>"; var bodyBytes = Encoding.ASCII.GetBytes(body); state.HttpResponse.ContentLength = bodyBytes.Length; state.HttpResponse.Close(bodyBytes); }
/// <summary> /// This method receives requests from other peers and maps the URI /// namespace or resource name to an actual namespace handler. If one /// exists then the namespace handler is invoked to process the request. /// If not some default processing is used. /// </summary> /// <param name="asyncResult">The Async object for the request.</param> public void GetRequestCallback(IAsyncResult asyncResult) { try { OnBeforeProcessRequest(); var stateObject = asyncResult.AsyncState as HttpApplication; stateObject.HttpRequest = _listener.EndGetRequest(asyncResult); if (stateObject.HttpRequest.Method == "GET") { var ns = stateObject.HttpRequest.RequestUri.Segments[1]; if (_nsModules.Contains(ns)) { var nsHandler = (IHttpNamespaceHandler) _nsModules[ns]; nsHandler.ProcessRequest(stateObject); } else { HandleUnsupportedNamespace(stateObject); } } else if (stateObject.HttpRequest.Method == "POST") { if (stateObject.HttpRequest.ContentLength <= 0) { var message = "<html><body>Bad Request - POST with no data"; message += "</body></html>"; HandleBadRequest(stateObject, message); } else { stateObject.RequestStream = stateObject.HttpRequest.GetRequestStream(); try { stateObject.RequestStream.BeginRead(stateObject.ReadBuffer, 0, stateObject.ReadBuffer.Length, _readCB, stateObject); } catch (Exception exception) { #if DEBUG if (HttpTraceHelper.ExceptionCaught.TraceVerbose) { HttpTraceHelper.WriteLine("Caught exception:" + exception); } #endif } } } else { HandleUnsupportedNamespace(stateObject); } // Queue up another _listener var newStateObject = new HttpApplication(); _listener.BeginGetRequest(_getRequestCB, newStateObject); } catch (Exception ex) { #if DEBUG if (HttpTraceHelper.ExceptionCaught.TraceVerbose) { HttpTraceHelper.WriteLine("Caught exception:" + ex); } #endif var stateObject = asyncResult.AsyncState as HttpApplication; stateObject.Reset(); _listener.BeginGetRequest(_getRequestCB, stateObject); } finally { OnAfterProcessRequest(); } }
/// <summary> /// Processes the HTTP Request. This handler is capable of /// processing several different messages and a series of /// conditional logic is used to determine which message /// is being invoked. /// </summary> /// <param name="webapp">The web application object for the request.</param> public void ProcessRequest(HttpApplication webapp) { // Initiailize any locals. This sets up a basic successful // response and cahces the requested namespace for the conditional // logic. var requestedNamespace = webapp.HttpRequest.RequestUri.AbsolutePath; webapp.HttpResponse.Server = "Microsoft .Net Terrarium"; webapp.HttpResponse.Date = DateTime.Now; webapp.HttpResponse.ContentType = "text/xml"; webapp.HttpResponse.StatusCode = HttpStatusCode.OK; webapp.HttpResponse.StatusDescription = "OK"; webapp.HttpResponse.KeepAlive = false; var failureReason = "none"; string body; if (webapp.HttpRequest.Method == "GET") { // Code to implement the GET method. The /organisms/stats namespace // is the important namespace here, and all other namespaces return // an error. if (requestedNamespace == "/organisms/stats") { // Gets XML statistics information from the // network engine. body = _engine.GetNetworkStatistics(); } else { body = "<HTML><BODY>" + "Sorry, GET is not supported for "; body += webapp.HttpRequest.RequestUri.ToString(); body += "</BODY></HTML>"; webapp.HttpResponse.ContentType = "text/html"; // Return an error stream. The namespace being requested // doesn't actually exist. This is a BadRequest webapp.HttpResponse.StatusCode = HttpStatusCode.BadRequest; } } else if (webapp.HttpRequest.Method == "POST") { // Code to implement the POST method. Most of the resources // provided by the OrganismNamespaceHandler have to be // retrieved using the POST method. // Prepares a binary formatter to serialize/deserialize // state information. var channel = new BinaryFormatter(); // Add a special binder to the BinaryFormatter to ensure that // the stream hasn't been hacked to try to get us to instantiate an // object that the organism shouldn't be able to access channel.Binder = new TeleportStateBinder(); TeleportState theOrganism = null; if (requestedNamespace == "/organisms/state") { _engine.WriteProtocolInfo("/organisms/state: Start receiving TeleportState."); // Provides an implementation for the /organisms/state // resource. This resource handles the retrieval of // a state object that will be placed into the game // engine (Step 4 of the conversation). var exceptionOccurred = false; var ipAddress = webapp.HttpRequest.RemoteEndPoint.Address.ToString(); // If they aren't on the same channel, set the exception bit // this will cause the teleportation to fail if (webapp.HttpRequest.Headers["peerChannel"].ToUpper(CultureInfo.InvariantCulture) != GameEngine.Current.PeerChannel.ToUpper(CultureInfo.InvariantCulture)) { exceptionOccurred = true; failureReason = "Peer channel mismatch"; _engine.WriteProtocolInfo("/organisms/state: Sender is wrong peer channel. Denied."); } if (_engine.PeerManager.BadPeer(ipAddress) || !_engine.PeerManager.ShouldReceive(ipAddress)) { exceptionOccurred = true; failureReason = "The peer " + ipAddress + " did not pass the check for badpeer/shouldreceive on the remote peer"; _engine.WriteProtocolInfo( "/organisms/state: Sender is marked as a bad peer or is sending too often. Denied."); } else { try { theOrganism = (TeleportState) channel.Deserialize(webapp.Buffer); } catch (Exception e) { ErrorLog.LogHandledException(e); GameEngine.Current.NetworkEngine.LastTeleportationException = e.ToString(); exceptionOccurred = true; failureReason = "Exception occured during deserialization of the organism state"; } } if (!exceptionOccurred) { _engine.WriteProtocolInfo("/organisms/state: TeleportState successfully deserialized."); // Check to see if the assembly is installed locally Debug.Assert(GameEngine.Current != null); Debug.Assert(theOrganism.OrganismState != null); Debug.Assert(theOrganism.OrganismState.Species != null); Debug.Assert(((Species) theOrganism.OrganismState.Species).AssemblyInfo != null); if ( GameEngine.Current.Pac.Exists( ((Species) theOrganism.OrganismState.Species).AssemblyInfo.FullName)) { _engine.WriteProtocolInfo("/organisms/state: Assembly exists, add organism to game."); // Add the teleported organism to the game GameEngine.Current.ReceiveTeleportation(theOrganism, false); // Let the peer know that we don't need the assembly body = "<assemblyreceived>true</assemblyreceived>"; } else { _engine.WriteProtocolInfo( "/organisms/state: Assembly doesn't exist, don't add organism to game."); // Let the peer know that we'll need the assembly body = "<assemblyreceived>false</assemblyreceived>"; } _engine.PeerManager.SetReceive(ipAddress); } else { _engine.WriteProtocolInfo("/organisms/state: Problem occurred:" + failureReason); body = "<organismArrived>false</organismArrived><reason>" + failureReason + "</reason>"; GameEngine.Current.NetworkEngine.CountFailedTeleportationReceives(); } } else if (requestedNamespace == "/organisms/assemblies") { _engine.WriteProtocolInfo("/organisms/assemblies: Start receiving organism assembly"); // Someone is sending us an assembly to save try { var ipAddress = webapp.HttpRequest.RemoteEndPoint.Address.ToString(); if (_engine.PeerManager.BadPeer(ipAddress) || !_engine.PeerManager.ShouldReceive(ipAddress)) { _engine.WriteProtocolInfo( "/organisms/assemblies: Sender is marked as a bad peer or is sending too often. Denied."); body = "<assemblysaved>false</assemblysaved>"; } else { // Write the assembly to a location that is "safe" meaning that it is not // a location that is known or predictable in any way. This prevents an attack // where an assembly is sent to this machine with malicious code in it and saved // in a known location that can be used at a later time. var tempFile = PrivateAssemblyCache.GetSafeTempFileName(); using (Stream fileStream = File.OpenWrite(tempFile)) { var bufferBytes = webapp.Buffer.GetBuffer(); fileStream.Write(bufferBytes, 0, bufferBytes.Length); } // Now save the assembly in the Private Assembly Cache var assemblyFullName = webapp.HttpRequest.Headers["Assembly"]; try { GameEngine.Current.Pac.SaveOrganismAssembly(tempFile, assemblyFullName); _engine.WriteProtocolInfo("/organisms/assemblies: Assembly saved in PAC."); } catch (Exception e) { // The assembly could fail validation ErrorLog.LogHandledException(e); GameEngine.Current.NetworkEngine.LastTeleportationException = e.ToString(); GameEngine.Current.NetworkEngine.CountFailedTeleportationReceives(); } File.Delete(tempFile); body = "<assemblysaved>true</assemblysaved>"; } } catch (Exception ex) { ErrorLog.LogHandledException(ex); body = "<assemblysaved>false</assemblysaved>"; GameEngine.Current.NetworkEngine.LastTeleportationException = ex.ToString(); GameEngine.Current.NetworkEngine.CountFailedTeleportationReceives(); } } else if (requestedNamespace == "/organisms/assemblycheck") { _engine.WriteProtocolInfo("/organisms/assemblycheck: Checking to see if we have an assembly."); // Check to see if the assembly is installed locally var reader = new StreamReader(webapp.Buffer, Encoding.ASCII); var assemblyName = reader.ReadToEnd(); Debug.Assert(GameEngine.Current != null); if (GameEngine.Current.Pac.Exists(assemblyName)) { // Let the peer know that we don't need the assembly _engine.WriteProtocolInfo("/organisms/assemblycheck: We have it."); body = "<assemblyexists>true</assemblyexists>"; } else { // Let the peer know that we need the assembly _engine.WriteProtocolInfo("/organisms/assemblycheck: Don't have it."); body = "<assemblyexists>false</assemblyexists>"; } } else { // Handles unsupported namespaces. This could have been // offloaded to a helper function in the HttpNamespaceManager. body = "<HTML><BODY>" + "The namespace " + webapp.HttpRequest.RequestUri; body += " is not supported.</BODY></HTML>"; webapp.HttpResponse.ContentType = "text/html"; } } else { // Handles rendering of unsupport methods. This could have been // offloaded to a helper function in the HttpNamespaceManager // or another class. webapp.HttpResponse.StatusCode = HttpStatusCode.MethodNotAllowed; webapp.HttpResponse.StatusDescription = "Method Not Allowed"; body = "<HTML><BODY>" + "The method " + webapp.HttpRequest.Method; body += " is not allowed.</BODY></HTML>"; webapp.HttpResponse.ContentType = "text/html"; } // Encode the body response and output the response. var bodyBytes = Encoding.ASCII.GetBytes(body); webapp.HttpResponse.ContentLength = bodyBytes.Length; webapp.HttpResponse.Close(bodyBytes); }
/// <summary> /// Start listening on the given host IP Address, on the given /// port. /// </summary> /// <param name="hostIP">The host's IP address to bind to.</param> /// <param name="port">The port to bind on.</param> public void Start(string hostIP, int port) { HttpTraceHelper.ExceptionCaught.Level = TraceLevel.Verbose; //TraceHelper.Api.Level = TraceLevel.Verbose; //TraceHelper.ExceptionThrown.Level = TraceLevel.Verbose; //TraceHelper.InternalLog.Level = TraceLevel.Verbose; //TraceHelper.Protocol.Level = TraceLevel.Verbose; //TraceHelper.Socket.Level = TraceLevel.Verbose; listener = new HttpWebListener(); listener.Start(port); // Put this in a loop if we want more listeners in the pool HttpApplication stateObject = new HttpApplication(); listener.BeginGetRequest(getRequestCB, stateObject); }