267 lines
9.6 KiB
C#
267 lines
9.6 KiB
C#
//------------------------------------------------------------------------------
|
|
// 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<UnicodePipeContract.Exp:READY> Stdin;
|
|
|
|
[OutputEndpoint("data")]
|
|
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
|
|
|
|
[Endpoint]
|
|
public readonly TRef<DirectoryServiceContract.Imp:Start> 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:<application> run application.\n";
|
|
usageString += " -apparg:<argument> supply argument to application.\n";
|
|
usageString += " -path:<physicalpath>\n";
|
|
usageString += " -port:<port>\n";
|
|
usageString += " -quitURL quit if URL ending in \"quit\" is received.\n";
|
|
usageString += " [-silent]\n";
|
|
usageString += " [-verbose] show files served\n";
|
|
usageString += " [-vpath:<virtualpath>]\n";
|
|
usageString += " [-client:<clientIP>]\n";
|
|
|
|
ShowMessage(usageString);
|
|
}
|
|
|
|
private static void ShowMessage(String msg) {
|
|
Console.WriteLine("Visual Web Developer Web Server:\n" + msg);
|
|
}
|
|
}
|
|
}
|