//------------------------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ using System; using System.Diagnostics; using Microsoft.VisualStudio.WebHost; using System.Globalization; using Microsoft.Singularity; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.WebApps.Contracts; using Microsoft.Contracts; using Microsoft.SingSharp.Reflection; using Microsoft.Singularity.Applications; [assembly: Transform(typeof(ApplicationResourceTransform))] namespace Microsoft.VisualStudio.WebServer { [ConsoleCategory(HelpMessage="cassini [options] A web server", DefaultAction=true)] internal sealed class Parameters { [InputEndpoint("data")] public readonly TRef Stdin; [OutputEndpoint("data")] public readonly TRef Stdout; [Endpoint] public readonly TRef nsRef; [BoolParameter("v", Default=false, HelpMessage="Verbose")] internal bool verbose; [BoolParameter("s", Default=false, HelpMessage="Silent")] internal bool silent; [BoolParameter("quitURL", Default=false, HelpMessage="Allow special URL to terminate")] internal bool quitURL; [StringParameter("port", Default=null, HelpMessage="Port ID to listen to")] internal string portString; [StringParameter("vpath", Default="", HelpMessage="Root of Virtual Path")] internal string virtualPath; [StringParameter("client", Default="", HelpMessage="Client IP")] internal string client; [StringParameter("apparg", Default="", HelpMessage="Arg to pass to client app")] internal string appArg; [StringParameter("app", Mandatory=true, Default="", HelpMessage="Name of the app to run")] internal string app; [StringParameter("nspath", Mandatory=false, Default=null, HelpMessage="NS path to app to run")] internal string nsPath; [LongParameter("load", Mandatory=false, Default=1000, HelpMessage="Overload timeout threshold (in ms)")] internal long load; reflective internal Parameters(); internal int AppMain() { return WebServerApp.AppMain(this); } } public sealed class WebServerApp { internal static int AppMain(Parameters! config) { bool verbose = config.verbose; bool silent = config.silent; string virtualPath = config.virtualPath; if (virtualPath != null) { virtualPath = virtualPath.Trim(); } if ((virtualPath == null) || (virtualPath.Length == 0)) { virtualPath = "/"; } else { if (virtualPath.StartsWith("/") == false) { if (!silent) { ShowUsage(); } return -1; } } string physicalPath = "\\"; // //TODO: HACKHACK FIXME: the file system is not wired up //in Singularity; avoid using it! // //string physicalPath = (string)commandLine.Options["path"]; //if (physicalPath != null) { // physicalPath = physicalPath.Trim(); //} //if ((physicalPath == null) || (physicalPath.Length == 0)) { // if (!silent) { // ShowUsage(); // } // return -1; //} //else { // if (Directory.Exists(physicalPath) == false) { // if (!silent) { // ShowMessage("The physical path '"+ physicalPath + "' does not exist!"); // } // return -2; // } // // // added this to resolve paths like "." // physicalPath = Path.GetFullPath(physicalPath); //} // int port = 0; string portText = config.portString; if (portText != null) { portText = portText.Trim(); } if ((portText != null) && (portText.Length != 0)) { try { port = Int32.Parse(portText); if ((port < 1) || (port > 65535)) { if (!silent) { ShowUsage(); } return -1; } } catch { if (!silent) { ShowMessage("Invalid port '" + portText + "'"); } return -3; } } else { port = 80; } // clientIP was added so that we could test using Cassini remotely on // lab machines. This now allows the client to be localhost or clientIP. string clientIP = config.client; if (clientIP != null) { clientIP = clientIP.Trim(); } if ((clientIP == null) || (clientIP.Length == 0)) { clientIP = "localhost"; } // // Singularity-specific initialization // string webApp = config.app; bool quitURL = config.quitURL; // FIXME: should generalize this so more than one arg can be passed to // the webapp! string[] appArgs = null; if (config.appArg != null) { appArgs = new string[1]; appArgs[0] = config.appArg; } if ((webApp == null) || (webApp.Length == 0)) { webApp = "HelloWebApp"; } webApp.Trim(); // If a nsPath was provide, then connect to the client. if (config.nsPath != null) { Console.WriteLine("Cassini: Connecting to {0}.", config.nsPath); DirectoryServiceContract.Imp! dsImp = config.nsRef.Acquire(); dsImp.RecvSuccess(); WebAppContract.Imp! waImp; WebAppContract.Exp! waExp; WebAppContract.NewChannel(out waImp, out waExp); ErrorCode error; if (!SdsUtils.Bind(config.nsPath, dsImp, waExp, out error)) { DebugStub.WriteLine("Failed to bind to webapp store...error {0}\n", __arglist(SdsUtils.ErrorCodeToString(error))); Console.WriteLine("Failed to bind to webapp store...error {0}\n", SdsUtils.ErrorCodeToString(error)); delete dsImp; delete waImp; return -1; } delete dsImp; waImp.RecvWebAppReady(); Dispatcher.BalanceConnection(waImp, config.load); } if (!Dispatcher.Initialize(webApp, appArgs, verbose, quitURL)) { if (!silent) { ShowMessage("Invalid web application name \"" + webApp + "\""); return -5; } } // ==================== // Actually start the server below // =================== try { Server server = new Server(port, virtualPath, physicalPath, clientIP); server.Start(); Console.WriteLine(); Console.WriteLine("Running Web Server on port {0}.", port); Console.WriteLine("Application '{0}' is mapped to '{1}'.", virtualPath, physicalPath); Console.WriteLine("http://localhost:{0}{1}", port, virtualPath); // Currently no console-read support in Singularity. Run forever! //Console.WriteLine("Hit Enter to stop the server"); //Console.ReadLine(); //server.Stop(); } catch (Exception ex) { if (!silent) { ShowMessage("Error opening port " + port + ": " + ex.Message); } return -5; } return 0; } private static void ShowUsage() { string usageString; usageString = "Invalid usage.\n\n"; usageString += "cassini [options]\n"; usageString += "Options:\n"; usageString += " -app: run application.\n"; usageString += " -apparg: supply argument to application.\n"; usageString += " -path:\n"; usageString += " -port:\n"; usageString += " -quitURL quit if URL ending in \"quit\" is received.\n"; usageString += " [-silent]\n"; usageString += " [-verbose] show files served\n"; usageString += " [-vpath:]\n"; usageString += " [-client:]\n"; ShowMessage(usageString); } private static void ShowMessage(String msg) { Console.WriteLine("Visual Web Developer Web Server:\n" + msg); } } }