2008-11-17 18:29:00 -05:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2008-03-05 09:52:00 -05:00
|
|
|
//
|
|
|
|
// Microsoft Research Singularity
|
|
|
|
//
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//
|
|
|
|
// Note:
|
2008-11-17 18:29:00 -05:00
|
|
|
// This file holds the Singularity-specific web application dispatcher
|
|
|
|
// logic that knows how to invoke web applications under Singularity.
|
2008-03-05 09:52:00 -05:00
|
|
|
//
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Collections;
|
2008-11-17 18:29:00 -05:00
|
|
|
using System.Web;
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
using Microsoft.Singularity.Channels;
|
|
|
|
using Microsoft.Singularity.WebApps;
|
|
|
|
using Microsoft.Singularity.WebApps.Contracts;
|
|
|
|
|
|
|
|
namespace Microsoft.Singularity.WebServer
|
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
/// <summary>
|
|
|
|
/// WEB Application class with static lookup/create method and request processor.
|
|
|
|
/// </summary>
|
2008-03-05 09:52:00 -05:00
|
|
|
internal sealed class Application
|
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
/// <summary>
|
|
|
|
/// Name to Application Cache, including failed-to-start appliccations.
|
|
|
|
/// </summary>
|
2008-03-05 09:52:00 -05:00
|
|
|
private static Hashtable applications = new Hashtable();
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
/// <summary>
|
|
|
|
/// Map an Request to an Application Name.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// Currently, this method simply takes the FilePath after the leading slash.
|
|
|
|
/// <para>
|
|
|
|
/// Eventually this should be based on manifests and
|
|
|
|
/// the mapping configuration of the WebServer which
|
|
|
|
/// are passed to the WebServer at startup.
|
|
|
|
/// </para>
|
|
|
|
/// </remarks>
|
|
|
|
private static string ApplicationNameFromRequest(Request! request)
|
|
|
|
{
|
|
|
|
return request.GetFilePath().Substring(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Return an Application or null given a Request.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="request">
|
|
|
|
/// Request, used here to determine particular Application.
|
|
|
|
/// </param>
|
|
|
|
/// <returns>
|
|
|
|
/// Application (normally) or null (if Application not found or failed to start).
|
|
|
|
/// </returns>
|
|
|
|
/// <remarks>
|
|
|
|
/// Keeps a cache of Applications (including non-starters).
|
|
|
|
/// </remarks>
|
2008-03-05 09:52:00 -05:00
|
|
|
public static Application FindOrCreateApplication(Request! request)
|
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
string applicationName = Application.ApplicationNameFromRequest(request);
|
|
|
|
|
|
|
|
Application application = (Application)applications[applicationName];
|
|
|
|
|
|
|
|
if (application == null) {
|
|
|
|
// Assure no races during insertion of applications into the hashtable.
|
2008-03-05 09:52:00 -05:00
|
|
|
lock (applications) {
|
2008-11-17 18:29:00 -05:00
|
|
|
// Retrieve again under lock in case another
|
|
|
|
// thread just added the same application.
|
|
|
|
application = (Application)applications[applicationName];
|
|
|
|
if (application == null) {
|
|
|
|
// Create and record the application in the set of applications
|
|
|
|
// and then try to start it. This avoids repeated create/start
|
|
|
|
// attempts on every client request when Start() fails.
|
|
|
|
application = new Application(applicationName);
|
|
|
|
applications[applicationName] = application;
|
|
|
|
application.Start();
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
|
|
|
|
// return only sucessfully started applications (see above).
|
|
|
|
return (application.started) ? application : null;
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
private TRef<WebAppContract.Imp:ProcessingState> wrapAppConn;
|
|
|
|
private string! name;
|
|
|
|
private bool started;
|
|
|
|
|
|
|
|
// statistics
|
2008-11-17 18:29:00 -05:00
|
|
|
private ulong totalServed = 0ul;
|
|
|
|
private ulong totalTime = 0ul;
|
2008-03-05 09:52:00 -05:00
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
private Application(string! name)
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
|
|
|
this.name = name;
|
|
|
|
this.started = false;
|
|
|
|
base();
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
/// <summary>
|
|
|
|
/// Process given a Request.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="request">
|
|
|
|
/// Request, used here to determine particular Application.
|
|
|
|
/// Passed to the application for further analysis.
|
|
|
|
/// </param>
|
2008-03-05 09:52:00 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
private void Start()
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
|
|
|
WebAppContract.Imp! appConnImp;
|
|
|
|
WebAppContract.Exp! appConnExp;
|
|
|
|
WebAppContract.NewChannel(out appConnImp, out appConnExp);
|
|
|
|
|
|
|
|
try {
|
2008-11-17 18:29:00 -05:00
|
|
|
string[] processArguments = new string[] {name};
|
|
|
|
Process child = new Process(processArguments, (Endpoint * in ExHeap) appConnExp);
|
2008-03-05 09:52:00 -05:00
|
|
|
child.Start();
|
|
|
|
appConnImp.RecvWebAppReady();
|
|
|
|
wrapAppConn = new TRef<WebAppContract.Imp:ProcessingState>(appConnImp);
|
2008-11-17 18:29:00 -05:00
|
|
|
started = true;
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
catch (Exception) {
|
2008-11-17 18:29:00 -05:00
|
|
|
// TODO: The exception story of Singularity needs work.
|
|
|
|
// Since the member 'started' is initialized to 'false'
|
|
|
|
// do nothing here. Eventually log the failure w/ ETS.
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
// TODO: Why is this subclass here when it only contains a single
|
|
|
|
// static method? Should it be merged with the Application class?
|
2008-03-05 09:52:00 -05:00
|
|
|
internal class RequestExporter
|
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
public static void ServiceRequestChannel(IHttpRequest! request,
|
|
|
|
HttpRequestContract.Exp! conn)
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
|
|
|
bool done = false;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
while (!done) {
|
2008-03-05 09:52:00 -05:00
|
|
|
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));
|
2008-11-17 18:29:00 -05:00
|
|
|
}
|
|
|
|
else {
|
2008-03-05 09:52:00 -05:00
|
|
|
conn.SendHeaderValue(null);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case conn.GetBody() :
|
|
|
|
byte[] bodyData = request.GetBodyData();
|
|
|
|
|
|
|
|
if (bodyData != null) {
|
|
|
|
conn.SendBodyData(Bitter.FromByteArray(bodyData));
|
2008-11-17 18:29:00 -05:00
|
|
|
}
|
|
|
|
else {
|
2008-03-05 09:52:00 -05:00
|
|
|
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;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
case conn.SendHeader(char[]! in ExHeap nameChars,
|
|
|
|
char[]! in ExHeap valChars) :
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|