// // Methods... // /// <summary> /// Save a file (& generate C# code) /// /// Called by an incoming http connection => /// THREADSAFETY: Called from web server thread => do not call Unity code from this thread! /// </summary> /// <param name="data">Data.</param> public void Save(Dictionary <string, string> data) { string filename = data ["file"]; string content = data ["content"]; string codecontent = data ["code"]; ServerLog.Log("Saving \"" + filename + "\""); string fullpath = Path.Combine(scriptOutputDir, filename); string directory = Path.GetDirectoryName(fullpath); // Save the file Directory.CreateDirectory(directory); SaveEntry entry = new SaveEntry(); entry.filename = "Assets/" + filename; entry.content = content; entry.code = VubbiFileHandler.GenerateHeader() + codecontent; lock (lockThis) { specificAssetsToSave.Add(entry); } // Note: if the save throws an error we will never know (we are in a different thread and we are not waiting for the save) }
/// <summary> /// Initializes a new instance of the <see cref="vubbiscript.VubbiServer"/> class. /// /// This instance handles the incoming HTTP GET & POST requests with logic specific for Vubbi /// </summary> /// <param name="config">Config.</param> /// <param name="api">API.</param> public VubbiServer(ServerConfig config, VubbiApi api) : base(config.IP, config.Port) { this.config = config; this.api = api; try { Thread thread = new Thread(new ThreadStart(this.listen)); thread.Start(); ServerLog.Log("Server Running"); } catch (Exception e) { ServerLog.Log("Server Start Exception"); ServerLog.Log(e); } }
public void process() { // we can't use a StreamReader for input, because it buffers up extra data on us inside it's // "processed" view of the world, and we want the data raw after the headers inputStream = new BufferedStream(socket.GetStream()); // we probably shouldn't be using a streamwriter for all output from handlers either outputStream = new StreamWriter(new BufferedStream(socket.GetStream())); try { // Keep processing requests from the browser (keep alive) while (true) { parseRequest(); readHeaders(); if (http_method.Equals("GET")) { handleGETRequest(); } else if (http_method.Equals("POST")) { handlePOSTRequest(); } outputStream.Flush(); } } catch (EndOfStreamException eose) { if (eose.Message == "EOS_BROWSER") { return; } } catch (Exception e) { ServerLog.Log("Exception: " + e.ToString()); writeFailure(); outputStream.Flush(); } finally { // bs.Flush(); // flush any remaining output inputStream = null; outputStream = null; // bs = null; socket.Close(); } }
public void listen() { try{ listener = new TcpListener(address, port); listener.Start(); while (is_active) { ServerLog.LogInfo("Listening!"); TcpClient s = listener.AcceptTcpClient(); ServerLog.LogInfo("Client accepted!"); s.ReceiveTimeout = 1000; // After 1 seconds, time out the read operation!!! // Why? Because as long as the operation is waiting, this thread is in "native code" and Unity cannot go to "play" mode or reload the scripts! HttpProcessor processor = new HttpProcessor(s, this); Thread thread = new Thread(new ThreadStart(processor.process)); thread.Start(); Thread.Sleep(1); } } catch (Exception e) { ServerLog.Log("Server Listening Exception!"); ServerLog.Log(e); } }
/// <summary> /// HTTP POST => Implement to do API calls (saving/loading/deleting/listing Vubbi files) /// </summary> /// <param name="p">P.</param> /// <param name="inputData">Input data.</param> public override void handlePOSTRequest(HttpProcessor p, StreamReader inputData) { if (p.http_url.Equals("/api/save")) { // Save the file... api.Save(ParseQuery(inputData)); SendApiOk(p, ""); return; } if (p.http_url.Equals("/api/load")) { // Save the file... String content = api.Load(ParseQuery(inputData)); SendApiOk(p, content); return; } if (p.http_url.Equals("/api/lang")) { // Send back the language preference of the user... SendApiOk(p, api.GetLanguagePreference()); return; } if (p.http_url.StartsWith("/api/setlang/")) { // Send back the language preference of the user... api.SetLanguagePreference(p.http_url.Substring("/api/setlang/".Length)); SendApiOk(p, ""); return; } ServerLog.Log(p.http_url); String errorMessage = "<H2>Http Post not supported yet...</H2>"; SendHeader(config.DefaultMime, errorMessage.Length, ServerHttpStatusCode.ERR_404_FILENOTFOUND, p); p.outputStream.Write(errorMessage); }
/// <summary> /// /// This code is called when the Unity Editor starts. It starts Vubbi (installation + web server). /// /// </summary> static Init() { // // Check command line arguments... // string commandlinebuildkeyword = "+exportvubbiscriptunitypackage"; if (System.Environment.GetCommandLineArgs().Contains(commandlinebuildkeyword)) { // So, we need to do a build instead of running the code... string[] cargs = System.Environment.GetCommandLineArgs(); string outputdir = null; for (int i = 0; i < cargs.Length; i++) { if (cargs [i] == commandlinebuildkeyword) { outputdir = cargs [i + 1]; } } AssetDatabase.ExportPackage("Assets/Editor Default Resources/Editor/VubbiScript", outputdir, ExportPackageOptions.Recurse); return; // DO NOT CONTINUE... Exit Unity now... } // // Set the icon for Vubbi files // VubbiFileHandler.InitVubbiIcon(); // // Server Configuration // ServerConfig config = new ServerConfig(); #if UNITY_EDITOR // When running in development mode => the "web" code can be found in the folder "web" next to the actual unity project folder config.HostDirectory = System.IO.Path.Combine(UnityEngine.Application.dataPath, "../../web"); #else // When compiling for the release build, we do not define UNITY_EDITOR // For released code, the HTML & JavaScript can be found in: config.HostDirectory = System.IO.Path.Combine(UnityEngine.Application.dataPath, "../WebRoot"); #endif config.Port = 8040; // // Update Web directory // This is part of the "installer"... // Right after importing the unity package, we will unzip the data file and put it in the main project folder. // => Resulting in a "WebRoot" folder // string dataFile = System.IO.Path.Combine(UnityEngine.Application.dataPath, "Editor Default Resources/Editor/VubbiScript/Vubbi.data"); if (System.IO.File.Exists(dataFile)) { System.IO.Directory.CreateDirectory(config.HostDirectory + "/"); UnzipDataDir(dataFile, config.HostDirectory + "/"); System.IO.File.Delete(dataFile); AssetDatabase.Refresh(); ServerLog.Log("Installation successful!"); } // // Start Server // VubbiApi api = new VubbiApi(); new VubbiServer(config, api); // // Register update callback // EditorApplication.update += api.OnUpdate; }
/// <summary> /// HTTP GET => Implement to load actual files from the server... Returns the html/js/css/... files. /// </summary> /// <param name="p">P.</param> public override void handleGETRequest(HttpProcessor p) { String requestedUrl = p.http_url; ServerLog.Log(String.Format("request: {0}", requestedUrl)); if (requestedUrl.EndsWith("/")) { // We are looking in a directory... } //Extract the requested file name String directory = requestedUrl.Substring(requestedUrl.IndexOf("/"), requestedUrl.LastIndexOf("/") - requestedUrl.IndexOf("/")); String filename = requestedUrl.Substring(requestedUrl.LastIndexOf("/") + 1); ///////////////////////////////////////////////////////////////////// // Identify the Physical Directory ///////////////////////////////////////////////////////////////////// String physicalDirectory = config.GetPhysicalPath(directory); // TODO: check if directory exists... ///////////////////////////////////////////////////////////////////// // Identify the File Name ///////////////////////////////////////////////////////////////////// //If The file name is not supplied then look in the default file list if (filename.Length == 0) { // Get the default filename filename = config.GetDefaultFileInFolder(physicalDirectory); } // TODO: check if filename is no longer "" and check if file exists... String mimeHeader = config.GetMimeType(filename); String fullPath = Path.Combine(physicalDirectory, filename); ServerLog.LogInfo(" < " + fullPath); if (!File.Exists(fullPath)) { ServerLog.Log(" [!]" + ServerHttpStatusCode.ERR_404_FILENOTFOUND); String errorMessage = "<H2>404 Error! File Does Not Exists...</H2>"; SendHeader(config.DefaultMime, errorMessage.Length, ServerHttpStatusCode.ERR_404_FILENOTFOUND, p); p.outputStream.Write(errorMessage); } else { ServerLog.LogInfo(" [+]" + HttpStatusCode.OK); FileInfo f = new FileInfo(fullPath); Stream filecontent = new BufferedStream(f.OpenRead()); try{ SendHeader(mimeHeader, f.Length, ServerHttpStatusCode.OK, p); p.outputStream.Flush(); CopyStream(filecontent, p.socket.GetStream()); }finally{ filecontent.Close(); } } }