singrdk/base/Applications/WebServer/Application.sg

266 lines
10 KiB
Plaintext
Raw Normal View History

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;
}
}
}
}
}
}