239 lines
8.6 KiB
Plaintext
239 lines
8.6 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 System.Collections;
|
||
|
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.Singularity.WebServer
|
||
|
{
|
||
|
internal sealed class Application
|
||
|
{
|
||
|
private static Hashtable applications = new Hashtable();
|
||
|
|
||
|
public static Application FindOrCreateApplication(Request! request)
|
||
|
{
|
||
|
// For now, we have an extremely lame way of mapping from a request
|
||
|
// to the appropriate application. We simply take the FilePath
|
||
|
// after the leading slash. That better be the name of a launchable
|
||
|
// application.
|
||
|
//
|
||
|
// TODO: Eventually this will be based on manifests and the mapping
|
||
|
// configuration of the WebServer, passed to the WebServer at
|
||
|
// startup.
|
||
|
|
||
|
string filePath = request.GetFilePath();
|
||
|
string appName = filePath.Substring(1);
|
||
|
|
||
|
Application app = (Application) applications[appName];
|
||
|
|
||
|
if (app == null) {
|
||
|
// No races around inserting unstarted applications into the hashtable.
|
||
|
lock (applications) {
|
||
|
// Perhaps it's there now.
|
||
|
app = (Application) applications[appName];
|
||
|
if (app == null) {
|
||
|
app = new Application(appName);
|
||
|
|
||
|
// Regardless of whether the app starts successfully,
|
||
|
// record it in our set of applications.
|
||
|
app.Start();
|
||
|
applications[appName] = app;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// If an app failed to start, we keep it in the hashtable so we
|
||
|
// don't keep re-trying on every client request.
|
||
|
return ((app != null && app.started)
|
||
|
? app
|
||
|
: null);
|
||
|
}
|
||
|
|
||
|
private TRef<WebAppContract.Imp:ProcessingState> wrapAppConn;
|
||
|
private string! name;
|
||
|
private bool started;
|
||
|
|
||
|
// statistics
|
||
|
private ulong totalServed = 0;
|
||
|
private ulong totalTime = 0;
|
||
|
|
||
|
public Application(string! name)
|
||
|
{
|
||
|
this.name = name;
|
||
|
this.started = false;
|
||
|
base();
|
||
|
}
|
||
|
|
||
|
public void Process(Request! request)
|
||
|
{
|
||
|
ulong startTime = Processor.CycleCount;
|
||
|
|
||
|
// Make a local request object
|
||
|
IHttpRequest localRequest = new LocalHttpRequest(request);
|
||
|
|
||
|
HttpRequestContract.Imp! reqConnImp;
|
||
|
HttpRequestContract.Exp! reqConnExp;
|
||
|
HttpRequestContract.NewChannel(out reqConnImp, out reqConnExp);
|
||
|
|
||
|
WebAppContract.Imp appConn = wrapAppConn.Acquire();
|
||
|
|
||
|
try {
|
||
|
// Start processing this request
|
||
|
appConn.SendProcess(reqConnImp);
|
||
|
appConn.RecvOK();
|
||
|
}
|
||
|
finally {
|
||
|
wrapAppConn.Release(appConn);
|
||
|
}
|
||
|
|
||
|
// Start servicing the request contract
|
||
|
RequestExporter.ServiceRequestChannel(localRequest, reqConnExp);
|
||
|
|
||
|
// Clean up
|
||
|
delete reqConnExp;
|
||
|
|
||
|
request.CloseConnection();
|
||
|
|
||
|
totalServed++;
|
||
|
totalTime += Processor.CycleCount - startTime;
|
||
|
}
|
||
|
|
||
|
public void Start()
|
||
|
{
|
||
|
string[] args = new string[1];
|
||
|
|
||
|
args[0] = name;
|
||
|
|
||
|
WebAppContract.Imp! appConnImp;
|
||
|
WebAppContract.Exp! appConnExp;
|
||
|
WebAppContract.NewChannel(out appConnImp, out appConnExp);
|
||
|
|
||
|
try {
|
||
|
Process child = new Process(args, (Endpoint * in ExHeap) appConnExp);
|
||
|
child.Start();
|
||
|
appConnImp.RecvWebAppReady();
|
||
|
wrapAppConn = new TRef<WebAppContract.Imp:ProcessingState>(appConnImp);
|
||
|
}
|
||
|
catch (Exception) {
|
||
|
// TODO: The exception story of Singularity needs work. For now,
|
||
|
// just record the fact that this app won't start.
|
||
|
return;
|
||
|
}
|
||
|
started = true;
|
||
|
}
|
||
|
|
||
|
internal class RequestExporter
|
||
|
{
|
||
|
public static void ServiceRequestChannel(IHttpRequest! request, HttpRequestContract.Exp! 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.FromString(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() :
|
||
|
// This is unexpected
|
||
|
DebugStub.Break();
|
||
|
done = true;
|
||
|
break;
|
||
|
|
||
|
case unsatisfiable :
|
||
|
// This is even more unexpected
|
||
|
DebugStub.Break();
|
||
|
done = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|