258 lines
9.3 KiB
Plaintext
258 lines
9.3 KiB
Plaintext
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: Dispatcher.sg
|
|
//
|
|
// Note:
|
|
// This file holds the Singularity-specific web app dispatcher that knows
|
|
// how to invoke web apps under Singularity.
|
|
//
|
|
|
|
using System;
|
|
using Microsoft.SingSharp;
|
|
using Microsoft.SingSharp.Runtime;
|
|
using Microsoft.Singularity;
|
|
using Microsoft.Singularity.Channels;
|
|
using Microsoft.Singularity.V1.Services;
|
|
using Microsoft.Singularity.WebApps;
|
|
using Microsoft.Singularity.WebApps.Contracts;
|
|
using System.Diagnostics;
|
|
using System.Web;
|
|
|
|
namespace Microsoft.VisualStudio.WebHost
|
|
{
|
|
internal class Dispatcher
|
|
{
|
|
private static TRef<WebAppContract.Imp:ProcessingState> m_AppConn;
|
|
private static bool m_quitUrl;
|
|
private static bool m_verbose;
|
|
private static string[] m_childArgs;
|
|
|
|
private static ulong m_TotalLocalTime = 0;
|
|
private static ulong m_TotalRemoteTime = 0;
|
|
private static ulong m_TotalLocalHits = 0;
|
|
private static ulong m_TotalRemoteHits = 0;
|
|
|
|
private const string QUIT_URI = "quit";
|
|
|
|
public static void DispatchRequest(Request! request)
|
|
{
|
|
string uriPath = request.GetUriPath();
|
|
if (m_quitUrl && uriPath.Length >= QUIT_URI.Length &&
|
|
uriPath.Substring(uriPath.Length - QUIT_URI.Length) == QUIT_URI)
|
|
{
|
|
request.CloseConnection();
|
|
ProcessService.Stop(0);
|
|
}
|
|
|
|
// Make the appropriate request object
|
|
IHttpRequest localRequest = new LocalHttpRequest(request);
|
|
|
|
ulong started = Processor.CycleCount;
|
|
|
|
// Call out-of-proc (usual case)
|
|
HttpRequestContract.Imp! reqConnImp;
|
|
HttpRequestContract.Exp! reqConnExp;
|
|
HttpRequestContract.NewChannel(out reqConnImp, out reqConnExp);
|
|
|
|
WebAppContract.Imp appConn = m_AppConn.Acquire();
|
|
|
|
try
|
|
{
|
|
// Start processing this request
|
|
appConn.SendProcess(reqConnImp);
|
|
|
|
switch receive {
|
|
case appConn.OK():
|
|
break;
|
|
|
|
case appConn.ChannelClosed():
|
|
Console.WriteLine("Child died, attempting to restart.");
|
|
delete appConn;
|
|
appConn = StartChild();
|
|
break;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
m_AppConn.Release(appConn);
|
|
}
|
|
|
|
// Start servicing the request contract
|
|
RequestExporter.ServiceRequestChannel(localRequest, reqConnExp);
|
|
|
|
// Clean up
|
|
delete reqConnExp;
|
|
|
|
ulong elapsed = Processor.CycleCount - started;
|
|
if (m_verbose) Console.WriteLine("served \"" + request.GetUriPath() + "\" in " + elapsed + " cycles");
|
|
|
|
++m_TotalRemoteHits;
|
|
|
|
if (m_TotalRemoteHits > 1)
|
|
{
|
|
m_TotalRemoteTime += elapsed;
|
|
//Console.WriteLine("Average remote time: " + m_TotalRemoteTime / (m_TotalRemoteHits - 1));
|
|
}
|
|
else
|
|
{
|
|
//Console.WriteLine("First remote hit (cold cache)");
|
|
}
|
|
|
|
request.CloseConnection();
|
|
}
|
|
|
|
public static WebAppContract.Imp:ProcessingState StartChild()
|
|
{
|
|
// Start up the webapp so it's ready to respond to requests
|
|
WebAppContract.Imp! appConnImp;
|
|
WebAppContract.Exp! appConnExp;
|
|
WebAppContract.NewChannel(out appConnImp, out appConnExp);
|
|
|
|
// REVIEW. should really inspect the manifest and determine what
|
|
// endpoints are needed by the child process. For example,specweb99
|
|
// now needs both the WebAppContract and a directory service contract
|
|
Process child = new Process(m_childArgs, (Endpoint * in ExHeap)appConnExp);
|
|
child.Start();
|
|
appConnImp.RecvWebAppReady();
|
|
|
|
return appConnImp;
|
|
}
|
|
|
|
public static bool Initialize(string! appName,
|
|
string[] appArgs,
|
|
bool verbose,
|
|
bool quitUrl)
|
|
{
|
|
m_quitUrl = quitUrl;
|
|
m_verbose = verbose;
|
|
|
|
// Start up the webapp so it's ready to respond to requests
|
|
string[] args;
|
|
|
|
if (appArgs != null) {
|
|
args = new string[1 + appArgs.Length];
|
|
args[0] = appName;
|
|
|
|
for (int i = 0; i < appArgs.Length; ++i) {
|
|
args[i + 1] = appArgs[i];
|
|
}
|
|
} else {
|
|
args = new string[1];
|
|
args[0] = appName;
|
|
}
|
|
m_childArgs = args;
|
|
|
|
WebAppContract.Imp! appConnImp = StartChild();
|
|
if (appConnImp == null) {
|
|
throw new Exception(String.Format("Could not create process {0}",args[0]));
|
|
}
|
|
|
|
m_AppConn = new TRef<WebAppContract.Imp:ProcessingState>(appConnImp);
|
|
|
|
return true;
|
|
}
|
|
|
|
internal class RequestExporter
|
|
{
|
|
public static void ServiceRequestChannel(IHttpRequest! request,
|
|
HttpRequestContract.Exp:Start
|
|
opt(HttpRequestContract.Completed)! conn)
|
|
{
|
|
bool done = false;
|
|
|
|
while(!done)
|
|
{
|
|
switch receive
|
|
{
|
|
case conn.GetUriPath() :
|
|
conn.SendUriPath(Bitter.FromString2(request.GetUriPath()));
|
|
break;
|
|
|
|
case conn.GetQueryString() :
|
|
conn.SendQueryString(Bitter.FromString(request.GetQueryString()));
|
|
break;
|
|
|
|
case conn.GetVerb() :
|
|
conn.SendVerb(Bitter.FromString2(request.GetVerb()));
|
|
break;
|
|
|
|
case conn.GetHeader(char[]! in ExHeap headerChars) :
|
|
string headerName = Bitter.ToString(headerChars);
|
|
delete headerChars;
|
|
string headerVal = request.GetHeader(headerName);
|
|
if (headerVal != null) {
|
|
conn.SendHeaderValue(Bitter.FromString(headerVal));
|
|
} else {
|
|
conn.SendHeaderValue(null);
|
|
}
|
|
break;
|
|
|
|
case conn.GetBody() :
|
|
byte[] bodyData = request.GetBodyData();
|
|
|
|
if (bodyData != null) {
|
|
conn.SendBodyData(Bitter.FromByteArray(bodyData));
|
|
} else {
|
|
conn.SendBodyData(null);
|
|
}
|
|
break;
|
|
|
|
case conn.GetRemoteAddress() :
|
|
conn.SendRemoteAddress(Bitter.FromString(request.GetRemoteAddress()));
|
|
break;
|
|
|
|
case conn.SendStatus(int code, char[]! in ExHeap descChars) :
|
|
{
|
|
string description = Bitter.ToString(descChars);
|
|
delete descChars;
|
|
request.SendStatus(code, description);
|
|
conn.SendOK();
|
|
}
|
|
break;
|
|
|
|
case conn.SendHeader(char[]! in ExHeap nameChars, char[]! in ExHeap valChars) :
|
|
{
|
|
string name = Bitter.ToString(nameChars);
|
|
delete nameChars;
|
|
|
|
string value = Bitter.ToString(valChars);
|
|
delete valChars;
|
|
|
|
request.SendHeader(name, value);
|
|
conn.SendOK();
|
|
}
|
|
break;
|
|
|
|
case conn.SendBodyData(byte[]! in ExHeap dataBytes) :
|
|
{
|
|
request.SendBodyData(dataBytes);
|
|
conn.SendOK();
|
|
}
|
|
break;
|
|
|
|
case conn.Done() :
|
|
request.Done();
|
|
done = true;
|
|
break;
|
|
|
|
case conn.ChannelClosed() :
|
|
request.Done();
|
|
done = true;
|
|
break;
|
|
|
|
case unsatisfiable :
|
|
// This is even more unexpected
|
|
DebugStub.Break();
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|