зеркало из https://github.com/mono/xsp.git
* IApplicationHost.cs: Added RequestBroker property to IApplicationHost.
The application host will query for information through this object, which lives in the main domain. * Makefile.am: Added new files. * ModMonoRequest.cs: Added position parameter in SendResponseFromMemory, so we can send partially filled buffers. GetClientBlock(): Some checks that where done in ModMonoWorkerRequest have been moved here. We can avoid two cross-app domain calls in this way. Merged SetStatusCode and SetStatusLine into SetStatusCodeLine. We avoid another cross-app domain call. * ModMonoWorkerRequest.cs: Renamed to ModMonoWorkerRequest, to make things more understandable. Removed all references to ModMonoRequest. All request information is now available as input parameters or through the IRequestBroker. Some old calls to ModMonoRequest have been merged into a single IRequestBroker call, to avoid the overhead of a cross-app domain call. * XSPApplicationHost.cs: Most of the code has been moved to other classes: ApplicationServer, WebSource, XSPWebSource, ModMonoWebSource... * XSPWorkerRequest.cs: Removed dependency to RequestData, since passing it through the cross-app domain channel requires serialization support. The same information is passed as primitive parameters. In general, calls to the network stream are now done through the IRequestBroker. * server.cs: Create the ApplicationServer by providing a ModMonoWebSource or a XSPWebSource instance. * ApplicationServer.cs: New file. Moved here the old XSPApplicationServer. Almost all code is now shared between XSP and mod_mono. Specific behavior is now encapsulated in an IWebSource object. * LingeringNetworkStream.cs: New file. Moved here the old MyNetworkStream. * BaseApplicationHost.cs: New file. Moved here the old XSPApplicationHost. * WebSource.cs: New file. Defines the IWebSource and IWorker interfaces. * XSPApplicationHost.cs: New file. Provides an implementation for XSP of a IWebSource, an IRequestBroker, an IApplicationHost and an IWorker. * ModMonoApplicationHost.cs: New file. Provides an implementation for mod_mono of a IWebSource, an IRequestBroker, an IApplicationHost and an IWorker. svn path=/trunk/xsp/; revision=29988
This commit is contained in:
Родитель
ac054ca27b
Коммит
f997661c65
52
ChangeLog
52
ChangeLog
|
@ -1,3 +1,55 @@
|
|||
2004-06-20 Lluis Sanchez Gual <lluis@ximian.com>
|
||||
|
||||
* IApplicationHost.cs: Added RequestBroker property to IApplicationHost.
|
||||
The application host will query for information through this object,
|
||||
which lives in the main domain.
|
||||
|
||||
* Makefile.am: Added new files.
|
||||
|
||||
* ModMonoRequest.cs: Added position parameter in SendResponseFromMemory, so
|
||||
we can send partially filled buffers.
|
||||
GetClientBlock(): Some checks that where done in ModMonoWorkerRequest
|
||||
have been moved here. We can avoid two cross-app domain calls in this
|
||||
way.
|
||||
Merged SetStatusCode and SetStatusLine into SetStatusCodeLine. We avoid
|
||||
another cross-app domain call.
|
||||
|
||||
* ModMonoWorkerRequest.cs: Renamed to ModMonoWorkerRequest, to make things
|
||||
more understandable.
|
||||
Removed all references to ModMonoRequest. All request information is
|
||||
now available as input parameters or through the IRequestBroker.
|
||||
Some old calls to ModMonoRequest have been merged into a single
|
||||
IRequestBroker call, to avoid the overhead of a cross-app domain call.
|
||||
|
||||
* XSPApplicationHost.cs: Most of the code has been moved to other classes:
|
||||
ApplicationServer, WebSource, XSPWebSource, ModMonoWebSource...
|
||||
|
||||
* XSPWorkerRequest.cs: Removed dependency to RequestData, since passing it
|
||||
through the cross-app domain channel requires serialization support.
|
||||
The same information is passed as primitive parameters.
|
||||
In general, calls to the network stream are now done through the
|
||||
IRequestBroker.
|
||||
|
||||
* server.cs: Create the ApplicationServer by providing a ModMonoWebSource
|
||||
or a XSPWebSource instance.
|
||||
|
||||
* ApplicationServer.cs: New file. Moved here the old XSPApplicationServer.
|
||||
Almost all code is now shared between XSP and mod_mono. Specific behavior
|
||||
is now encapsulated in an IWebSource object.
|
||||
|
||||
* LingeringNetworkStream.cs: New file. Moved here the old MyNetworkStream.
|
||||
|
||||
* BaseApplicationHost.cs: New file. Moved here the old XSPApplicationHost.
|
||||
|
||||
* WebSource.cs: New file. Defines the IWebSource and IWorker interfaces.
|
||||
|
||||
* XSPApplicationHost.cs: New file. Provides an implementation for XSP
|
||||
of a IWebSource, an IRequestBroker, an IApplicationHost and an IWorker.
|
||||
|
||||
* ModMonoApplicationHost.cs: New file. Provides an implementation for
|
||||
mod_mono of a IWebSource, an IRequestBroker, an IApplicationHost and an
|
||||
IWorker.
|
||||
|
||||
2004-06-19 Gonzalo Paniagua Javier <gonzalo@ximian.com>
|
||||
|
||||
* test/Makefile.am: install extensions.dll into test directory so that
|
||||
|
|
|
@ -0,0 +1,419 @@
|
|||
//
|
||||
// Mono.ASPNET.ApplicationServer
|
||||
//
|
||||
// Authors:
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
// Lluis Sanchez Gual (lluis@ximian.com)
|
||||
//
|
||||
// (C) Copyright 2004 Novell, Inc
|
||||
//
|
||||
|
||||
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using System.Xml;
|
||||
using System.Web;
|
||||
using System.Web.Hosting;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Mono.ASPNET
|
||||
{
|
||||
// ApplicationServer runs the main server thread, which accepts client
|
||||
// connections and forwards the requests to the correct web application.
|
||||
// ApplicationServer takes an IWebSource object as parameter in the
|
||||
// constructor. IWebSource provides methods for getting some objects
|
||||
// whose behavior is specific to XSP or mod_mono.
|
||||
|
||||
// Each web application lives in its own application domain, and incoming
|
||||
// requests are processed in the corresponding application domain.
|
||||
// Since the client Socket can't be passed from one domain to the other, the
|
||||
// flow of information must go through the cross-app domain channel.
|
||||
|
||||
// For each application two objects are created:
|
||||
// 1) a IApplicationHost object is created in the application domain
|
||||
// 2) a IRequestBroker is created in the main domain.
|
||||
//
|
||||
// The IApplicationHost is used by the ApplicationServer to start the
|
||||
// processing of a request in the application domain.
|
||||
// The IRequestBroker is used from the application domain to access
|
||||
// information in the main domain.
|
||||
//
|
||||
// The complete sequence of servicing a request is the following:
|
||||
//
|
||||
// 1) The listener accepts an incoming connection.
|
||||
// 2) An IWorker object is created (through the IWebSource), and it is
|
||||
// queued in the thread pool.
|
||||
// 3) When the IWorker's run method is called, it registers itself in
|
||||
// the application's request broker, and gets a request id. All this is
|
||||
// done in the main domain.
|
||||
// 4) The IWorker starts the request processing by making a cross-app domain
|
||||
// call to the application host. It passes as parameters the request id
|
||||
// and other information already read from the request.
|
||||
// 5) The application host executes the request. When it needs to read or
|
||||
// write request data, it performs remote calls to the request broker,
|
||||
// passing the request id provided by the IWorker.
|
||||
// 6) When the request broker receives a call from the application host,
|
||||
// it locates the IWorker registered with the provided request id and
|
||||
// forwards the call to it.
|
||||
|
||||
public class ApplicationServer
|
||||
{
|
||||
IWebSource webSource;
|
||||
bool started;
|
||||
bool stop;
|
||||
bool verbose;
|
||||
Socket listen_socket;
|
||||
|
||||
Thread runner;
|
||||
|
||||
// This is much faster than hashtable for typical cases.
|
||||
ArrayList vpathToHost = new ArrayList ();
|
||||
|
||||
public ApplicationServer (IWebSource source)
|
||||
{
|
||||
webSource = source;
|
||||
}
|
||||
|
||||
public bool Verbose {
|
||||
get { return verbose; }
|
||||
set { verbose = value; }
|
||||
}
|
||||
|
||||
private void AddApplication (string vhost, int vport, string vpath, string fullPath)
|
||||
{
|
||||
// TODO - check for duplicates, sort, optimize, etc.
|
||||
if (started)
|
||||
throw new InvalidOperationException ("The server is already started.");
|
||||
|
||||
if (verbose) {
|
||||
Console.WriteLine("Registering application:");
|
||||
Console.WriteLine(" Host: {0}", (vhost != null) ? vhost : "any");
|
||||
Console.WriteLine(" Port: {0}", (vport != -1) ?
|
||||
vport.ToString () : "any");
|
||||
|
||||
Console.WriteLine(" Virtual path: {0}", vpath);
|
||||
Console.WriteLine(" Physical path: {0}", fullPath);
|
||||
}
|
||||
|
||||
vpathToHost.Add (new VPathToHost (vhost, vport, vpath, fullPath));
|
||||
}
|
||||
|
||||
public void AddApplicationsFromConfigDirectory (string directoryName)
|
||||
{
|
||||
if (verbose) {
|
||||
Console.WriteLine ("Adding applications from *.webapp files in " +
|
||||
"directory '{0}'", directoryName);
|
||||
}
|
||||
|
||||
DirectoryInfo di = new DirectoryInfo (directoryName);
|
||||
foreach (FileInfo fi in di.GetFiles ("*.webapp")) {
|
||||
AddApplicationsFromConfigFile (fi.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddApplicationsFromConfigFile (string fileName)
|
||||
{
|
||||
if (verbose) {
|
||||
Console.WriteLine ("Adding applications from config file '{0}'", fileName);
|
||||
}
|
||||
|
||||
try {
|
||||
XmlDocument doc = new XmlDocument ();
|
||||
doc.Load (fileName);
|
||||
|
||||
foreach (XmlElement el in doc.SelectNodes ("//web-application")) {
|
||||
AddApplicationFromElement (el);
|
||||
}
|
||||
} catch {
|
||||
Console.WriteLine ("Error loading '{0}'", fileName);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void AddApplicationFromElement (XmlElement el)
|
||||
{
|
||||
XmlNode n;
|
||||
|
||||
string name = el.SelectSingleNode ("name").InnerText;
|
||||
string vpath = el.SelectSingleNode ("vpath").InnerText;
|
||||
string path = el.SelectSingleNode ("path").InnerText;
|
||||
|
||||
string vhost = null;
|
||||
n = el.SelectSingleNode ("vhost");
|
||||
#if !MOD_MONO_SERVER
|
||||
if (n != null)
|
||||
vhost = n.InnerText;
|
||||
#else
|
||||
// TODO: support vhosts in xsp.exe
|
||||
if (verbose)
|
||||
Console.WriteLine ("Ignoring vhost {0} for {1}", n.InnerText, name);
|
||||
#endif
|
||||
|
||||
int vport = -1;
|
||||
n = el.SelectSingleNode ("vport");
|
||||
#if !MOD_MONO_SERVER
|
||||
if (n != null)
|
||||
vport = Convert.ToInt32 (n.InnerText);
|
||||
#else
|
||||
// TODO: Listen on different ports
|
||||
if (verbose)
|
||||
Console.WriteLine ("Ignoring vport {0} for {1}", n.InnerText, name);
|
||||
#endif
|
||||
|
||||
AddApplication (vhost, vport, vpath, path);
|
||||
}
|
||||
|
||||
public void AddApplicationsFromCommandLine (string applications)
|
||||
{
|
||||
if (applications == null)
|
||||
throw new ArgumentNullException ("applications");
|
||||
|
||||
if (applications == "")
|
||||
return;
|
||||
|
||||
if (verbose) {
|
||||
Console.WriteLine("Adding applications '{0}'...", applications);
|
||||
}
|
||||
|
||||
string [] apps = applications.Split (',');
|
||||
|
||||
foreach (string str in apps) {
|
||||
string [] app = str.Split (':');
|
||||
|
||||
if (app.Length < 2 || app.Length > 4)
|
||||
throw new ArgumentException ("Should be something like " +
|
||||
"[[hostname:]port:]VPath:realpath");
|
||||
|
||||
int vport;
|
||||
string vhost;
|
||||
string vpath;
|
||||
string realpath;
|
||||
int pos = 0;
|
||||
|
||||
if (app.Length >= 3) {
|
||||
vhost = app[pos++];
|
||||
} else {
|
||||
vhost = null;
|
||||
}
|
||||
|
||||
if (app.Length >= 4) {
|
||||
// FIXME: support more than one listen port.
|
||||
vport = Convert.ToInt16 (app[pos++]);
|
||||
} else {
|
||||
vport = -1;
|
||||
}
|
||||
|
||||
vpath = app [pos++];
|
||||
realpath = app[pos++];
|
||||
|
||||
if (!vpath.EndsWith ("/"))
|
||||
vpath += "/";
|
||||
|
||||
string fullPath = System.IO.Path.GetFullPath (realpath);
|
||||
AddApplication (vhost, vport, vpath, fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Start (bool bgThread)
|
||||
{
|
||||
if (started)
|
||||
throw new InvalidOperationException ("The server is already started.");
|
||||
|
||||
if (vpathToHost == null)
|
||||
throw new InvalidOperationException ("SetApplications must be called first.");
|
||||
|
||||
if (vpathToHost.Count == 0)
|
||||
throw new InvalidOperationException ("No applications defined.");
|
||||
|
||||
listen_socket = webSource.CreateSocket ();
|
||||
listen_socket.Listen (5);
|
||||
runner = new Thread (new ThreadStart (RunServer));
|
||||
runner.IsBackground = bgThread;
|
||||
runner.Start ();
|
||||
stop = false;
|
||||
WebTrace.WriteLine ("Server started.");
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop ()
|
||||
{
|
||||
if (!started)
|
||||
throw new InvalidOperationException ("The server is not started.");
|
||||
|
||||
stop = true;
|
||||
listen_socket.Close ();
|
||||
lock (vpathToHost) {
|
||||
foreach (VPathToHost v in vpathToHost) {
|
||||
v.ClearHost ();
|
||||
}
|
||||
}
|
||||
WebTrace.WriteLine ("Server stopped.");
|
||||
}
|
||||
|
||||
private void RunServer ()
|
||||
{
|
||||
started = true;
|
||||
Socket client;
|
||||
while (!stop){
|
||||
client = listen_socket.Accept ();
|
||||
WebTrace.WriteLine ("Accepted connection.");
|
||||
IWorker worker = webSource.CreateWorker (client, this);
|
||||
ThreadPool.QueueUserWorkItem (new WaitCallback (worker.Run));
|
||||
}
|
||||
|
||||
started = false;
|
||||
}
|
||||
|
||||
public VPathToHost GetApplicationForPath (string vhost, int port, string path,
|
||||
bool defaultToRoot)
|
||||
{
|
||||
VPathToHost bestMatch = null;
|
||||
int bestMatchLength = 0;
|
||||
|
||||
// Console.WriteLine("GetApplicationForPath({0},{1},{2},{3})", vhost, port,
|
||||
// path, defaultToRoot);
|
||||
for (int i = vpathToHost.Count - 1; i >= 0; i--) {
|
||||
VPathToHost v = (VPathToHost) vpathToHost [i];
|
||||
int matchLength = v.vpath.Length;
|
||||
if (matchLength <= bestMatchLength || !v.Match (vhost, port, path))
|
||||
continue;
|
||||
|
||||
bestMatchLength = matchLength;
|
||||
bestMatch = v;
|
||||
}
|
||||
|
||||
if (bestMatch != null) {
|
||||
lock (bestMatch) {
|
||||
if (bestMatch.AppHost == null)
|
||||
bestMatch.CreateHost (webSource);
|
||||
}
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
if (defaultToRoot)
|
||||
return GetApplicationForPath (vhost, port, "/", false);
|
||||
|
||||
if (verbose)
|
||||
Console.WriteLine ("No application defined for: {0}:{1}{2}", vhost, port, path);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class VPathToHost
|
||||
{
|
||||
public readonly string vhost;
|
||||
public readonly int vport;
|
||||
public readonly string vpath;
|
||||
public readonly string realPath;
|
||||
public readonly bool haveWildcard;
|
||||
|
||||
public IApplicationHost AppHost;
|
||||
public IRequestBroker RequestBroker;
|
||||
|
||||
public VPathToHost (string vhost, int vport, string vpath, string realPath)
|
||||
{
|
||||
this.vhost = (vhost != null) ? vhost.ToLower (CultureInfo.InvariantCulture) : null;
|
||||
this.vport = vport;
|
||||
this.vpath = vpath;
|
||||
if (vpath == null || vpath == "" || vpath [0] != '/')
|
||||
throw new ArgumentException ("Virtual path must begin with '/': " + vpath,
|
||||
"vpath");
|
||||
|
||||
this.realPath = realPath;
|
||||
this.AppHost = null;
|
||||
if (vhost != null && vhost.Length != 0 && vhost [0] == '*') {
|
||||
haveWildcard = true;
|
||||
if (vhost.Length > 2 && vhost [1] == '.')
|
||||
vhost = vhost.Substring (2);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearHost ()
|
||||
{
|
||||
this.AppHost = null;
|
||||
}
|
||||
|
||||
public bool Match (string vhost, int vport, string vpath)
|
||||
{
|
||||
if (vport != -1 && this.vport != -1 && vport != this.vport)
|
||||
return false;
|
||||
|
||||
if (vhost != null && this.vhost != null) {
|
||||
int length = this.vhost.Length;
|
||||
if (haveWildcard) {
|
||||
if (this.vhost == "*")
|
||||
return true;
|
||||
|
||||
length = vhost.Length;
|
||||
}
|
||||
|
||||
if (length != vhost.Length ||
|
||||
!this.vhost.EndsWith (vhost.ToLower (CultureInfo.InvariantCulture))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int local = vpath.Length;
|
||||
int vlength = this.vpath.Length;
|
||||
if (vlength > local) {
|
||||
// Check for /xxx requests to be redirected to /xxx/
|
||||
if (this.vpath [vlength - 1] != '/')
|
||||
return false;
|
||||
|
||||
return (vlength - 1 == local && this.vpath.Substring (0, vlength - 1) == vpath);
|
||||
}
|
||||
|
||||
return (vpath.StartsWith (this.vpath));
|
||||
}
|
||||
|
||||
public void CreateHost (IWebSource webSource)
|
||||
{
|
||||
string v = vpath;
|
||||
if (v != "/" && v.EndsWith ("/")) {
|
||||
v = v.Substring (0, v.Length - 1);
|
||||
}
|
||||
|
||||
AppHost = ApplicationHost.CreateApplicationHost (webSource.GetApplicationHostType(), v, realPath) as IApplicationHost;
|
||||
|
||||
// Link the host in the application domain with a request broker in the main domain
|
||||
RequestBroker = webSource.CreateRequestBroker ();
|
||||
AppHost.RequestBroker = RequestBroker;
|
||||
}
|
||||
}
|
||||
|
||||
class HttpErrors
|
||||
{
|
||||
static byte [] error500;
|
||||
|
||||
static HttpErrors ()
|
||||
{
|
||||
string s = "HTTP/1.0 500 Server error\r\n\r\n" +
|
||||
"<html><head><title>500 Server Error</title><body><h1>Server error</h1>\r\n" +
|
||||
"Your client sent a request that was not understood by this server.\r\n" +
|
||||
"</body></html>\r\n";
|
||||
error500 = Encoding.Default.GetBytes (s);
|
||||
}
|
||||
|
||||
public static byte [] NotFound (string uri)
|
||||
{
|
||||
string s = String.Format ("HTTP/1.0 404 Not Found\r\n\r\n" +
|
||||
"<html><head><title>404 Not Found</title></head>\r\n" +
|
||||
"<body><h1>Not Found</h1>The requested URL {0} was not found on this " +
|
||||
"server.<p>\r\n</body></html>\r\n", uri);
|
||||
|
||||
return Encoding.ASCII.GetBytes (s);
|
||||
}
|
||||
|
||||
public static byte [] ServerError ()
|
||||
{
|
||||
return error500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
//
|
||||
// Mono.ASPNET.BaseApplicationHost
|
||||
//
|
||||
// Authors:
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
// Lluis Sanchez Gual (lluis@ximian.com)
|
||||
//
|
||||
// (C) Copyright 2004 Novell, Inc
|
||||
//
|
||||
using System;
|
||||
|
||||
namespace Mono.ASPNET
|
||||
{
|
||||
public class BaseApplicationHost : MarshalByRefObject, IApplicationHost
|
||||
{
|
||||
string path;
|
||||
string vpath;
|
||||
IRequestBroker requestBroker;
|
||||
EndOfRequestHandler endOfRequest;
|
||||
|
||||
public BaseApplicationHost ()
|
||||
{
|
||||
endOfRequest = new EndOfRequestHandler (EndOfRequest);
|
||||
}
|
||||
|
||||
public override object InitializeLifetimeService ()
|
||||
{
|
||||
return null; // who wants to live forever?
|
||||
}
|
||||
|
||||
public string Path {
|
||||
get {
|
||||
if (path == null)
|
||||
path = AppDomain.CurrentDomain.GetData (".appPath").ToString ();
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public string VPath {
|
||||
get {
|
||||
if (vpath == null)
|
||||
vpath = AppDomain.CurrentDomain.GetData (".appVPath").ToString ();
|
||||
|
||||
return vpath;
|
||||
}
|
||||
}
|
||||
|
||||
public AppDomain Domain {
|
||||
get { return AppDomain.CurrentDomain; }
|
||||
}
|
||||
|
||||
public IRequestBroker RequestBroker
|
||||
{
|
||||
get { return requestBroker; }
|
||||
set { requestBroker = value; }
|
||||
}
|
||||
|
||||
protected void ProcessRequest (MonoWorkerRequest mwr)
|
||||
{
|
||||
if (!mwr.ReadRequestData ()) {
|
||||
EndOfRequest (mwr);
|
||||
return;
|
||||
}
|
||||
|
||||
mwr.EndOfRequestEvent += endOfRequest;
|
||||
mwr.ProcessRequest ();
|
||||
}
|
||||
|
||||
public void EndOfRequest (MonoWorkerRequest mwr)
|
||||
{
|
||||
try {
|
||||
mwr.CloseConnection ();
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// Mono.ASPNET.BaseRequestBroker
|
||||
//
|
||||
// Authors:
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
// Lluis Sanchez Gual (lluis@ximian.com)
|
||||
//
|
||||
// (C) Copyright 2004 Novell, Inc
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Mono.ASPNET
|
||||
{
|
||||
public class BaseRequestBroker: MarshalByRefObject, IRequestBroker
|
||||
{
|
||||
ArrayList requests = new ArrayList ();
|
||||
Queue freeSlots = new Queue ();
|
||||
|
||||
internal int RegisterRequest (IWorker worker)
|
||||
{
|
||||
lock (requests)
|
||||
{
|
||||
if (freeSlots.Count == 0)
|
||||
return requests.Add (worker);
|
||||
|
||||
int freeSlot = (int)freeSlots.Dequeue ();
|
||||
requests [freeSlot] = worker;
|
||||
return freeSlot;
|
||||
}
|
||||
}
|
||||
|
||||
internal void UnregisterRequest (int id)
|
||||
{
|
||||
lock (requests)
|
||||
{
|
||||
requests [id] = null;
|
||||
freeSlots.Enqueue (id);
|
||||
}
|
||||
}
|
||||
|
||||
public int Read (int requestId, int size, out byte[] buffer)
|
||||
{
|
||||
buffer = new byte[size];
|
||||
IWorker w;
|
||||
lock (requests) {
|
||||
w = (IWorker) requests [requestId];
|
||||
}
|
||||
int nread = w.Read (buffer, 0, size);
|
||||
return nread;
|
||||
}
|
||||
|
||||
public IWorker GetWorker (int requestId)
|
||||
{
|
||||
lock (requests) {
|
||||
return (IWorker) requests [requestId];
|
||||
}
|
||||
}
|
||||
|
||||
public void Write (int requestId, byte[] buffer, int position, int size)
|
||||
{
|
||||
GetWorker (requestId).Write (buffer, position, size);
|
||||
}
|
||||
|
||||
public void Close (int requestId)
|
||||
{
|
||||
GetWorker (requestId).Close ();
|
||||
}
|
||||
|
||||
public void Flush (int requestId)
|
||||
{
|
||||
GetWorker (requestId).Flush ();
|
||||
}
|
||||
|
||||
public override object InitializeLifetimeService ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
namespace Mono.ASPNET
|
||||
{
|
||||
public interface IApplicationHost
|
||||
|
@ -16,6 +17,11 @@ namespace Mono.ASPNET
|
|||
string Path { get; }
|
||||
string VPath { get; }
|
||||
AppDomain Domain { get; }
|
||||
IRequestBroker RequestBroker { get; set; }
|
||||
}
|
||||
|
||||
public interface IRequestBroker
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// Mono.ASPNET.IWebSource
|
||||
//
|
||||
// Authors:
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
// Lluis Sanchez Gual (lluis@ximian.com)
|
||||
//
|
||||
// (C) Copyright 2004 Novell, Inc
|
||||
//
|
||||
|
||||
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Mono.ASPNET
|
||||
{
|
||||
public interface IWebSource
|
||||
{
|
||||
Socket CreateSocket ();
|
||||
IWorker CreateWorker (Socket client, ApplicationServer server);
|
||||
Type GetApplicationHostType ();
|
||||
IRequestBroker CreateRequestBroker ();
|
||||
}
|
||||
|
||||
public interface IWorker
|
||||
{
|
||||
void Run (object state);
|
||||
int Read (byte[] buffer, int position, int size);
|
||||
void Write (byte[] buffer, int position, int size);
|
||||
void Close ();
|
||||
void Flush ();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// Mono.ASPNET.ApplicationServer
|
||||
//
|
||||
// Authors:
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
//
|
||||
// (C) Copyright 2004 Novell, Inc
|
||||
//
|
||||
|
||||
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Mono.ASPNET
|
||||
{
|
||||
public class LingeringNetworkStream : NetworkStream
|
||||
{
|
||||
const int useconds_to_linger = 2000000;
|
||||
const int max_useconds_to_linger = 30000000;
|
||||
bool enableLingering = true;
|
||||
|
||||
public LingeringNetworkStream (Socket sock, bool owns) : base (sock, owns)
|
||||
{
|
||||
}
|
||||
|
||||
public bool EnableLingering
|
||||
{
|
||||
get { return enableLingering; }
|
||||
set { enableLingering = value; }
|
||||
}
|
||||
|
||||
void LingeringClose ()
|
||||
{
|
||||
int waited = 0;
|
||||
byte [] buffer = null;
|
||||
|
||||
Socket.Shutdown (SocketShutdown.Send);
|
||||
while (waited < max_useconds_to_linger) {
|
||||
int nread = 0;
|
||||
try {
|
||||
if (!Socket.Poll (useconds_to_linger, SelectMode.SelectRead))
|
||||
break;
|
||||
|
||||
if (buffer == null)
|
||||
buffer = new byte [512];
|
||||
|
||||
nread = Socket.Receive (buffer, 0, buffer.Length, 0);
|
||||
} catch { }
|
||||
|
||||
if (nread == 0)
|
||||
break;
|
||||
|
||||
waited += useconds_to_linger;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
if (enableLingering) {
|
||||
try {
|
||||
LingeringClose ();
|
||||
} finally {
|
||||
base.Close ();
|
||||
}
|
||||
}
|
||||
else
|
||||
base.Close ();
|
||||
}
|
||||
|
||||
public bool Connected {
|
||||
get { return Socket.Connected; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,13 +18,18 @@ modmono_references= -r:System.Web.dll -r:Mono.Posix.dll
|
|||
endif
|
||||
|
||||
common = IApplicationHost.cs \
|
||||
XSPApplicationHost.cs \
|
||||
MonoWorkerRequest.cs \
|
||||
Tracing.cs \
|
||||
ApplicationServer.cs \
|
||||
LingeringNetworkStream.cs \
|
||||
BaseApplicationHost.cs \
|
||||
BaseRequestBroker.cs \
|
||||
IWebSource.cs \
|
||||
server.cs
|
||||
|
||||
xsp_only = AssemblyInfo.cs.in \
|
||||
InitialWorkerRequest.cs \
|
||||
XSPApplicationHost.cs \
|
||||
XSPWorkerRequest.cs
|
||||
|
||||
xsp_sources = $(common) $(xsp_only:.in=)
|
||||
|
@ -32,6 +37,8 @@ xsp_build_sources = $(addprefix $(srcdir)/, $(xsp_sources))
|
|||
|
||||
modmono_only = ModMonoRequest.cs \
|
||||
ModMonoWorkerRequest.cs \
|
||||
ModMonoApplicationHost.cs \
|
||||
ModMonoRequest.cs \
|
||||
AssemblyInfoModMono.cs.in
|
||||
|
||||
modmono_sources = $(common) $(modmono_only:.in=)
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
//
|
||||
// Mono.ASPNET.ModMonoApplicationHost
|
||||
//
|
||||
// Authors:
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
// Lluis Sanchez Gual (lluis@ximian.com)
|
||||
//
|
||||
// (C) Copyright 2004 Novell, Inc
|
||||
//
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Mono.Posix;
|
||||
|
||||
namespace Mono.ASPNET
|
||||
{
|
||||
//
|
||||
// ModMonoWebSource: Provides methods to get objects and types specific
|
||||
// to mod_mono.
|
||||
//
|
||||
public class ModMonoWebSource: IWebSource
|
||||
{
|
||||
string filename;
|
||||
|
||||
public void SetListenFile (string filename)
|
||||
{
|
||||
if (filename == null)
|
||||
throw new ArgumentNullException ("filename");
|
||||
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public Socket CreateSocket ()
|
||||
{
|
||||
if (filename == null)
|
||||
throw new InvalidOperationException ("filename not set");
|
||||
|
||||
File.Delete (filename);
|
||||
Socket listen_socket = new Socket (AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
|
||||
EndPoint ep = new UnixEndPoint (filename);
|
||||
listen_socket.Bind (ep);
|
||||
return listen_socket;
|
||||
}
|
||||
|
||||
public IWorker CreateWorker (Socket client, ApplicationServer server)
|
||||
{
|
||||
return new ModMonoWorker (client, server);
|
||||
}
|
||||
|
||||
public Type GetApplicationHostType ()
|
||||
{
|
||||
return typeof (ModMonoApplicationHost);
|
||||
}
|
||||
|
||||
public IRequestBroker CreateRequestBroker ()
|
||||
{
|
||||
return new ModMonoRequestBroker ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ModMonoRequestBroker: The request broker for mod_mono. Provides some
|
||||
// additional methods to the BaseRequestBroker from which inherits.
|
||||
//
|
||||
public class ModMonoRequestBroker: BaseRequestBroker
|
||||
{
|
||||
public string GetServerVariable (int requestId, string name)
|
||||
{
|
||||
return ((ModMonoWorker)GetWorker (requestId)).GetServerVariable (name);
|
||||
}
|
||||
|
||||
public void SetStatusCodeLine (int requestId, int code, string status)
|
||||
{
|
||||
((ModMonoWorker)GetWorker (requestId)).SetStatusCodeLine (code, status);
|
||||
}
|
||||
|
||||
public void SetResponseHeader (int requestId, string name, string value)
|
||||
{
|
||||
((ModMonoWorker)GetWorker (requestId)).SetResponseHeader (name, value);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ModMonoApplicationHost: The application host for mod_mono.
|
||||
//
|
||||
public class ModMonoApplicationHost : BaseApplicationHost
|
||||
{
|
||||
public void ProcessRequest (int reqId, string verb, string queryString, string path, string protocol, string localAddress, int serverPort, string remoteAddress, int remotePort, string remoteName, string[] headers, string[] headerValues)
|
||||
{
|
||||
ModMonoRequestBroker broker = (ModMonoRequestBroker) RequestBroker;
|
||||
ModMonoWorkerRequest mwr = new ModMonoWorkerRequest (reqId, broker, this, verb, path, queryString, protocol, localAddress, serverPort, remoteAddress, remotePort, remoteName, headers, headerValues);
|
||||
ProcessRequest (mwr);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ModMonoWorker: The worker that do the initial processing of mod_mono
|
||||
// requests.
|
||||
//
|
||||
internal class ModMonoWorker: IWorker
|
||||
{
|
||||
ApplicationServer server;
|
||||
public LingeringNetworkStream Stream;
|
||||
ModMonoRequest modRequest;
|
||||
|
||||
public ModMonoWorker (Socket client, ApplicationServer server)
|
||||
{
|
||||
Stream = new LingeringNetworkStream (client, true);
|
||||
Stream.EnableLingering = false;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public void Run (object state)
|
||||
{
|
||||
int requestId = -1;
|
||||
ModMonoRequestBroker broker = null;
|
||||
|
||||
try {
|
||||
RequestReader rr = new RequestReader (Stream);
|
||||
string vhost = rr.Request.GetRequestHeader ("Host");
|
||||
int port = -1;
|
||||
if (vhost != null) {
|
||||
int colon = vhost.IndexOf (':');
|
||||
if (colon != -1) {
|
||||
port = Int32.Parse (vhost.Substring (colon + 1));
|
||||
vhost = vhost.Substring (0, colon);
|
||||
} else {
|
||||
port = 80;
|
||||
}
|
||||
}
|
||||
|
||||
VPathToHost vapp = server.GetApplicationForPath (vhost, port, rr.GetUriPath (), false);
|
||||
ModMonoApplicationHost host = (ModMonoApplicationHost) vapp.AppHost;
|
||||
if (host == null) {
|
||||
rr.Decline ();
|
||||
return;
|
||||
}
|
||||
modRequest = rr.Request;
|
||||
|
||||
broker = (ModMonoRequestBroker) vapp.RequestBroker;
|
||||
requestId = broker.RegisterRequest (this);
|
||||
|
||||
host.ProcessRequest (requestId,
|
||||
modRequest.GetHttpVerbName(),
|
||||
modRequest.GetQueryString(),
|
||||
modRequest.GetUri(),
|
||||
modRequest.GetProtocol(),
|
||||
modRequest.GetLocalAddress(),
|
||||
modRequest.GetServerPort(),
|
||||
modRequest.GetRemoteAddress(),
|
||||
modRequest.GetRemotePort(),
|
||||
modRequest.GetRemoteName(),
|
||||
modRequest.GetAllHeaders(),
|
||||
modRequest.GetAllHeaderValues());
|
||||
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine (e);
|
||||
try {
|
||||
byte [] error = HttpErrors.ServerError ();
|
||||
Stream.Write (error, 0, error.Length);
|
||||
Stream.Close ();
|
||||
} catch {}
|
||||
}
|
||||
finally {
|
||||
if (requestId != -1)
|
||||
broker.UnregisterRequest (requestId);
|
||||
}
|
||||
}
|
||||
|
||||
public int Read (byte[] buffer, int position, int size)
|
||||
{
|
||||
return modRequest.GetClientBlock (buffer, position, size);
|
||||
}
|
||||
|
||||
public void Write (byte[] buffer, int position, int size)
|
||||
{
|
||||
modRequest.SendResponseFromMemory (buffer, position, size);
|
||||
}
|
||||
|
||||
public void Close ()
|
||||
{
|
||||
modRequest.Close ();
|
||||
}
|
||||
|
||||
public void Flush ()
|
||||
{
|
||||
modRequest.Flush ();
|
||||
}
|
||||
|
||||
public string GetServerVariable (string name)
|
||||
{
|
||||
return modRequest.GetServerVariable (name);
|
||||
}
|
||||
|
||||
public void SetStatusCodeLine (int code, string status)
|
||||
{
|
||||
modRequest.SetStatusCodeLine (code, status);
|
||||
}
|
||||
|
||||
public void SetResponseHeader (string name, string value)
|
||||
{
|
||||
modRequest.SetResponseHeader (name, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -181,11 +181,11 @@ namespace Mono.ASPNET
|
|||
return verb;
|
||||
}
|
||||
|
||||
public void SendResponseFromMemory (byte [] data, int length)
|
||||
public void SendResponseFromMemory (byte [] data, int position, int length)
|
||||
{
|
||||
SendSimpleCommand (Cmd.SEND_FROM_MEMORY);
|
||||
writer.Write (length);
|
||||
writer.Write (data, 0, length);
|
||||
writer.Write (data, position, length);
|
||||
ReadEnd ();
|
||||
}
|
||||
|
||||
|
@ -352,8 +352,11 @@ namespace Mono.ASPNET
|
|||
return (i == 0);
|
||||
}
|
||||
|
||||
public int GetClientBlock ([Out] byte [] bytes, int size)
|
||||
public int GetClientBlock ([Out] byte [] bytes, int position, int size)
|
||||
{
|
||||
if (!ShouldClientBlock ()) return 0;
|
||||
if (SetupClientBlock () != 0) return 0;
|
||||
|
||||
SendSimpleCommand (Cmd.GET_CLIENT_BLOCK);
|
||||
writer.Write (size);
|
||||
ReadEnd ();
|
||||
|
@ -361,18 +364,14 @@ namespace Mono.ASPNET
|
|||
if (i > size)
|
||||
throw new Exception ("Houston...");
|
||||
|
||||
return reader.Read (bytes, 0, i);
|
||||
return reader.Read (bytes, position, i);
|
||||
}
|
||||
|
||||
public void SetStatusCode (int code)
|
||||
public void SetStatusCodeLine (int code, string status)
|
||||
{
|
||||
SendSimpleCommand (Cmd.SET_STATUS_CODE);
|
||||
writer.Write (code);
|
||||
ReadEnd ();
|
||||
}
|
||||
|
||||
public void SetStatusLine (string status)
|
||||
{
|
||||
SendSimpleCommand (Cmd.SET_STATUS_LINE);
|
||||
WriteString (status);
|
||||
ReadEnd ();
|
||||
|
|
|
@ -94,15 +94,24 @@ namespace Mono.ASPNET
|
|||
}
|
||||
}
|
||||
|
||||
public class XSPWorkerRequest : MonoWorkerRequest
|
||||
public class ModMonoWorkerRequest : MonoWorkerRequest
|
||||
{
|
||||
ModMonoRequest request;
|
||||
bool closed;
|
||||
string verb;
|
||||
string queryString;
|
||||
string protocol;
|
||||
string path;
|
||||
string pathInfo;
|
||||
string localAddress;
|
||||
int serverPort;
|
||||
string remoteAddress;
|
||||
int remotePort;
|
||||
string remoteName;
|
||||
ModMonoRequestBroker requestBroker;
|
||||
string[] headers;
|
||||
string[] headerValues;
|
||||
int requestId;
|
||||
|
||||
string [][] unknownHeaders;
|
||||
static string [] indexFiles = { "index.aspx",
|
||||
"Default.aspx",
|
||||
|
@ -110,7 +119,7 @@ namespace Mono.ASPNET
|
|||
"index.html",
|
||||
"index.htm" };
|
||||
|
||||
static XSPWorkerRequest ()
|
||||
static ModMonoWorkerRequest ()
|
||||
{
|
||||
string indexes = ConfigurationSettings.AppSettings ["MonoServerDefaultIndexFiles"];
|
||||
SetDefaultIndexFiles (indexes);
|
||||
|
@ -134,16 +143,27 @@ namespace Mono.ASPNET
|
|||
indexFiles = (string []) files.ToArray (typeof (string));
|
||||
}
|
||||
|
||||
public XSPWorkerRequest (NetworkStream ns, IApplicationHost appHost)
|
||||
public ModMonoWorkerRequest (int requestId, ModMonoRequestBroker requestBroker, IApplicationHost appHost, string verb, string path, string queryString, string protocol, string localAddress, int serverPort, string remoteAddress, int remotePort, string remoteName, string[] headers, string[] headerValues)
|
||||
: base (appHost)
|
||||
{
|
||||
this.request = new ModMonoRequest (ns);
|
||||
this.requestId = requestId;
|
||||
this.requestBroker = requestBroker;
|
||||
this.verb = verb;
|
||||
this.protocol = protocol;
|
||||
this.queryString = queryString;
|
||||
this.path = path;
|
||||
this.localAddress = localAddress;
|
||||
this.serverPort = serverPort;
|
||||
this.remoteAddress = remoteAddress;
|
||||
this.remotePort = remotePort;
|
||||
this.remoteName = remoteName;
|
||||
this.headers = headers;
|
||||
this.headerValues = headerValues;
|
||||
}
|
||||
|
||||
public XSPWorkerRequest (ModMonoRequest request, IApplicationHost appHost)
|
||||
: base (appHost)
|
||||
public int RequestId
|
||||
{
|
||||
this.request = request;
|
||||
get { return requestId; }
|
||||
}
|
||||
|
||||
public override string GetPathInfo ()
|
||||
|
@ -192,11 +212,6 @@ namespace Mono.ASPNET
|
|||
|
||||
protected override bool GetRequestData ()
|
||||
{
|
||||
verb = request.GetHttpVerbName ();
|
||||
protocol = request.GetProtocol ();
|
||||
queryString = request.GetQueryString ();
|
||||
|
||||
path = request.GetUri ();
|
||||
if (TryDirectory ()) {
|
||||
pathInfo = "";
|
||||
return true;
|
||||
|
@ -227,13 +242,13 @@ namespace Mono.ASPNET
|
|||
|
||||
public override void FlushResponse (bool finalFlush)
|
||||
{
|
||||
request.Flush ();
|
||||
requestBroker.Flush (requestId);
|
||||
}
|
||||
|
||||
public override void CloseConnection ()
|
||||
{
|
||||
if (!closed) {
|
||||
request.Close ();
|
||||
requestBroker.Close (requestId);
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
@ -250,12 +265,12 @@ namespace Mono.ASPNET
|
|||
|
||||
public override string GetLocalAddress ()
|
||||
{
|
||||
return request.GetLocalAddress ();
|
||||
return localAddress;
|
||||
}
|
||||
|
||||
public override int GetLocalPort ()
|
||||
{
|
||||
return request.GetServerPort ();
|
||||
return serverPort;
|
||||
}
|
||||
|
||||
public override string GetQueryString ()
|
||||
|
@ -265,33 +280,38 @@ namespace Mono.ASPNET
|
|||
|
||||
public override string GetRemoteAddress ()
|
||||
{
|
||||
return request.GetRemoteAddress ();
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
public override int GetRemotePort ()
|
||||
{
|
||||
return request.GetRemotePort ();
|
||||
return remotePort;
|
||||
}
|
||||
|
||||
public override string GetServerVariable (string name)
|
||||
{
|
||||
return request.GetServerVariable (name);
|
||||
return requestBroker.GetServerVariable (requestId, name);
|
||||
}
|
||||
|
||||
public override void SendResponseFromMemory (byte [] data, int length)
|
||||
{
|
||||
request.SendResponseFromMemory (data, length);
|
||||
if (data.Length > length * 2) {
|
||||
byte[] tmpbuffer = new byte[length];
|
||||
Array.Copy (data, tmpbuffer, length);
|
||||
requestBroker.Write (requestId, tmpbuffer, 0, length);
|
||||
}
|
||||
else
|
||||
requestBroker.Write (requestId, data, 0, length);
|
||||
}
|
||||
|
||||
public override void SendStatus (int statusCode, string statusDescription)
|
||||
{
|
||||
request.SetStatusCode (statusCode);
|
||||
request.SetStatusLine (String.Format("{0} {1}", statusCode, statusDescription));
|
||||
requestBroker.SetStatusCodeLine (requestId, statusCode, String.Format("{0} {1}", statusCode, statusDescription));
|
||||
}
|
||||
|
||||
public override void SendUnknownResponseHeader (string name, string value)
|
||||
{
|
||||
request.SetResponseHeader (name, value);
|
||||
requestBroker.SetResponseHeader (requestId, name, value);
|
||||
}
|
||||
|
||||
public override bool IsClientConnected ()
|
||||
|
@ -318,27 +338,24 @@ namespace Mono.ASPNET
|
|||
|
||||
public override string GetRemoteName ()
|
||||
{
|
||||
return request.GetRemoteName ();
|
||||
return remoteName;
|
||||
}
|
||||
|
||||
public override string GetUnknownRequestHeader (string name)
|
||||
{
|
||||
return request.GetRequestHeader (name);
|
||||
return GetRequestHeader (name);
|
||||
}
|
||||
|
||||
public override string [][] GetUnknownRequestHeaders ()
|
||||
{
|
||||
if (unknownHeaders == null) {
|
||||
string [] keys = request.GetAllHeaders ();
|
||||
string [] values = request.GetAllHeaderValues ();
|
||||
|
||||
int count = keys.Length;
|
||||
int count = headers.Length;
|
||||
ArrayList pairs = new ArrayList ();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (HttpWorkerRequest.GetKnownRequestHeaderIndex (keys [i]) != -1)
|
||||
if (HttpWorkerRequest.GetKnownRequestHeaderIndex (headers [i]) != -1)
|
||||
continue;
|
||||
|
||||
pairs.Add (new string [] { keys [i], values [i]});
|
||||
pairs.Add (new string [] { headers [i], headerValues [i]});
|
||||
}
|
||||
|
||||
if (pairs.Count != 0) {
|
||||
|
@ -353,7 +370,14 @@ namespace Mono.ASPNET
|
|||
|
||||
public override string GetKnownRequestHeader (int index)
|
||||
{
|
||||
return request.GetRequestHeader (GetKnownRequestHeaderName (index));
|
||||
return GetRequestHeader (GetKnownRequestHeaderName (index));
|
||||
}
|
||||
|
||||
private string GetRequestHeader (string name)
|
||||
{
|
||||
int i = Array.IndexOf (headers, name);
|
||||
if (i == -1) return null;
|
||||
else return headerValues [i];
|
||||
}
|
||||
|
||||
public override void SendCalculatedContentLength (int contentLength)
|
||||
|
@ -363,14 +387,13 @@ namespace Mono.ASPNET
|
|||
|
||||
public override int ReadEntityBody (byte [] buffer, int size)
|
||||
{
|
||||
if (buffer == null || size <= 0 || !request.ShouldClientBlock ())
|
||||
if (buffer == null || size <= 0)
|
||||
return 0;
|
||||
|
||||
int read = 0;
|
||||
if (request.SetupClientBlock () == 0)
|
||||
read = request.GetClientBlock (buffer, size);
|
||||
|
||||
return read;
|
||||
byte[] readBuffer;
|
||||
int nr = requestBroker.Read (requestId, size, out readBuffer);
|
||||
Array.Copy (readBuffer, 0, buffer, 0, nr);
|
||||
return nr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,357 +3,30 @@
|
|||
//
|
||||
// Authors:
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
// Lluis Sanchez Gual (lluis@ximian.com)
|
||||
//
|
||||
// (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
|
||||
// (C) Copyright 2004 Novell, Inc
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Xml;
|
||||
using System.Web.Hosting;
|
||||
using System.Runtime.Remoting.Lifetime;
|
||||
#if MODMONO_SERVER
|
||||
using Mono.Posix;
|
||||
#endif
|
||||
|
||||
namespace Mono.ASPNET
|
||||
{
|
||||
class HttpErrors
|
||||
//
|
||||
// XSPWebSource: Provides methods to get objects and types specific
|
||||
// to XSP.
|
||||
//
|
||||
public class XSPWebSource: IWebSource
|
||||
{
|
||||
static byte [] error500;
|
||||
|
||||
static HttpErrors ()
|
||||
IPEndPoint bindAddress;
|
||||
|
||||
public XSPWebSource ()
|
||||
{
|
||||
string s = "HTTP/1.0 500 Server error\r\n\r\n" +
|
||||
"<html><head><title>500 Server Error</title><body><h1>Server error</h1>\r\n" +
|
||||
"Your client sent a request that was not understood by this server.\r\n" +
|
||||
"</body></html>\r\n";
|
||||
error500 = Encoding.Default.GetBytes (s);
|
||||
}
|
||||
|
||||
public static byte [] NotFound (string uri)
|
||||
{
|
||||
string s = String.Format ("HTTP/1.0 404 Not Found\r\n\r\n" +
|
||||
"<html><head><title>404 Not Found</title></head>\r\n" +
|
||||
"<body><h1>Not Found</h1>The requested URL {0} was not found on this " +
|
||||
"server.<p>\r\n</body></html>\r\n", uri);
|
||||
|
||||
return Encoding.ASCII.GetBytes (s);
|
||||
}
|
||||
|
||||
public static byte [] ServerError ()
|
||||
{
|
||||
return error500;
|
||||
}
|
||||
}
|
||||
|
||||
public class MyNetworkStream : NetworkStream
|
||||
{
|
||||
const int useconds_to_linger = 2000000;
|
||||
const int max_useconds_to_linger = 30000000;
|
||||
|
||||
public MyNetworkStream (Socket sock, bool owns) : base (sock, owns)
|
||||
{
|
||||
}
|
||||
|
||||
#if !MODMONO_SERVER
|
||||
void LingeringClose ()
|
||||
{
|
||||
int waited = 0;
|
||||
byte [] buffer = null;
|
||||
|
||||
Socket.Shutdown (SocketShutdown.Send);
|
||||
while (waited < max_useconds_to_linger) {
|
||||
int nread = 0;
|
||||
try {
|
||||
if (!Socket.Poll (useconds_to_linger, SelectMode.SelectRead))
|
||||
break;
|
||||
|
||||
if (buffer == null)
|
||||
buffer = new byte [512];
|
||||
|
||||
nread = Socket.Receive (buffer, 0, buffer.Length, 0);
|
||||
} catch { }
|
||||
|
||||
if (nread == 0)
|
||||
break;
|
||||
|
||||
waited += useconds_to_linger;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
try {
|
||||
LingeringClose ();
|
||||
} finally {
|
||||
base.Close ();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
public bool Connected {
|
||||
get { return Socket.Connected; }
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class Worker
|
||||
{
|
||||
[NonSerialized] XSPApplicationServer server;
|
||||
IApplicationHost host;
|
||||
MyNetworkStream ns;
|
||||
EndOfRequestHandler endOfRequest;
|
||||
#if MODMONO_SERVER
|
||||
ModMonoRequest modRequest;
|
||||
#else
|
||||
RequestData rdata;
|
||||
EndPoint remoteEP;
|
||||
EndPoint localEP;
|
||||
#endif
|
||||
|
||||
public Worker (Socket client, EndPoint localEP, XSPApplicationServer server)
|
||||
{
|
||||
endOfRequest = new EndOfRequestHandler (EndOfRequest);
|
||||
ns = new MyNetworkStream (client, true);
|
||||
this.server = server;
|
||||
#if !MODMONO_SERVER
|
||||
try {
|
||||
remoteEP = client.RemoteEndPoint;
|
||||
} catch { }
|
||||
this.localEP = localEP;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Run (object state)
|
||||
{
|
||||
try {
|
||||
#if !MODMONO_SERVER
|
||||
if (remoteEP == null)
|
||||
return;
|
||||
|
||||
InitialWorkerRequest ir = new InitialWorkerRequest (ns);
|
||||
ir.ReadRequestData ();
|
||||
rdata = ir.RequestData;
|
||||
string vhost = null; // TODO: read the headers in InitialWorkerRequest
|
||||
int port = ((IPEndPoint) localEP).Port;
|
||||
host = server.GetApplicationForPath (vhost, port, rdata.Path, true);
|
||||
if (host == null) {
|
||||
byte [] nf = HttpErrors.NotFound (rdata.Path);
|
||||
ns.Write (nf, 0, nf.Length);
|
||||
ns.Close ();
|
||||
return;
|
||||
}
|
||||
#else
|
||||
RequestReader rr = new RequestReader (ns);
|
||||
string vhost = rr.Request.GetRequestHeader ("Host");
|
||||
int port = -1;
|
||||
if (vhost != null) {
|
||||
int colon = vhost.IndexOf (':');
|
||||
if (colon != -1) {
|
||||
port = Int32.Parse (vhost.Substring (colon + 1));
|
||||
vhost = vhost.Substring (0, colon);
|
||||
} else {
|
||||
port = 80;
|
||||
}
|
||||
}
|
||||
|
||||
host = server.GetApplicationForPath (vhost, port, rr.GetUriPath (), false);
|
||||
if (host == null) {
|
||||
rr.Decline ();
|
||||
return;
|
||||
}
|
||||
modRequest = rr.Request;
|
||||
#endif
|
||||
CrossAppDomainDelegate pr = new CrossAppDomainDelegate (ProcessRequest);
|
||||
host.Domain.DoCallBack (pr);
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine (e);
|
||||
try {
|
||||
byte [] error = HttpErrors.ServerError ();
|
||||
ns.Write (error, 0, error.Length);
|
||||
ns.Close ();
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessRequest ()
|
||||
{
|
||||
#if !MODMONO_SERVER
|
||||
XSPWorkerRequest mwr = new XSPWorkerRequest (ns, host, localEP, remoteEP, rdata);
|
||||
#else
|
||||
XSPWorkerRequest mwr = new XSPWorkerRequest (modRequest, host);
|
||||
#endif
|
||||
if (!mwr.ReadRequestData ()) {
|
||||
EndOfRequest (mwr);
|
||||
return;
|
||||
}
|
||||
|
||||
mwr.EndOfRequestEvent += endOfRequest;
|
||||
mwr.ProcessRequest ();
|
||||
}
|
||||
|
||||
public void EndOfRequest (MonoWorkerRequest mwr)
|
||||
{
|
||||
try {
|
||||
mwr.CloseConnection ();
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
public class XSPApplicationHost : MarshalByRefObject, IApplicationHost
|
||||
{
|
||||
string path;
|
||||
string vpath;
|
||||
|
||||
public override object InitializeLifetimeService ()
|
||||
{
|
||||
return null; // who wants to live forever?
|
||||
SetListenAddress (80);
|
||||
}
|
||||
|
||||
public string Path {
|
||||
get {
|
||||
if (path == null)
|
||||
path = AppDomain.CurrentDomain.GetData (".appPath").ToString ();
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public string VPath {
|
||||
get {
|
||||
if (vpath == null)
|
||||
vpath = AppDomain.CurrentDomain.GetData (".appVPath").ToString ();
|
||||
|
||||
return vpath;
|
||||
}
|
||||
}
|
||||
|
||||
public AppDomain Domain {
|
||||
get { return AppDomain.CurrentDomain; }
|
||||
}
|
||||
}
|
||||
|
||||
public class VPathToHost
|
||||
{
|
||||
public readonly string vhost;
|
||||
public readonly int vport;
|
||||
public readonly string vpath;
|
||||
public readonly string realPath;
|
||||
public readonly bool haveWildcard;
|
||||
|
||||
public IApplicationHost appHost;
|
||||
|
||||
public VPathToHost (string vhost, int vport, string vpath, string realPath)
|
||||
{
|
||||
this.vhost = (vhost != null) ? vhost.ToLower (CultureInfo.InvariantCulture) : null;
|
||||
this.vport = vport;
|
||||
this.vpath = vpath;
|
||||
if (vpath == null || vpath == "" || vpath [0] != '/')
|
||||
throw new ArgumentException ("Virtual path must begin with '/': " + vpath,
|
||||
"vpath");
|
||||
|
||||
this.realPath = realPath;
|
||||
this.appHost = null;
|
||||
if (vhost != null && vhost.Length != 0 && vhost [0] == '*') {
|
||||
haveWildcard = true;
|
||||
if (vhost.Length > 2 && vhost [1] == '.')
|
||||
vhost = vhost.Substring (2);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearHost ()
|
||||
{
|
||||
this.appHost = null;
|
||||
}
|
||||
|
||||
public bool Match (string vhost, int vport, string vpath)
|
||||
{
|
||||
if (vport != -1 && this.vport != -1 && vport != this.vport)
|
||||
return false;
|
||||
|
||||
if (vhost != null && this.vhost != null) {
|
||||
int length = this.vhost.Length;
|
||||
if (haveWildcard) {
|
||||
if (this.vhost == "*")
|
||||
return true;
|
||||
|
||||
length = vhost.Length;
|
||||
}
|
||||
|
||||
if (length != vhost.Length ||
|
||||
!this.vhost.EndsWith (vhost.ToLower (CultureInfo.InvariantCulture))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int local = vpath.Length;
|
||||
int vlength = this.vpath.Length;
|
||||
if (vlength > local) {
|
||||
// Check for /xxx requests to be redirected to /xxx/
|
||||
if (this.vpath [vlength - 1] != '/')
|
||||
return false;
|
||||
|
||||
return (vlength - 1 == local && this.vpath.Substring (0, vlength - 1) == vpath);
|
||||
}
|
||||
|
||||
return (vpath.StartsWith (this.vpath));
|
||||
}
|
||||
|
||||
public void CreateHost ()
|
||||
{
|
||||
string v = vpath;
|
||||
if (v != "/" && v.EndsWith ("/")) {
|
||||
v = v.Substring (0, v.Length - 1);
|
||||
}
|
||||
|
||||
this.appHost = ApplicationHost.CreateApplicationHost (
|
||||
typeof (XSPApplicationHost), v, realPath)
|
||||
as IApplicationHost;
|
||||
}
|
||||
}
|
||||
|
||||
public class XSPApplicationServer
|
||||
{
|
||||
Socket listen_socket;
|
||||
bool started;
|
||||
bool stop;
|
||||
bool verbose;
|
||||
|
||||
#if !MODMONO_SERVER
|
||||
IPEndPoint bindAddress;
|
||||
#endif
|
||||
Thread runner;
|
||||
|
||||
// This is much faster than hashtable for typical cases.
|
||||
ArrayList vpathToHost = new ArrayList ();
|
||||
|
||||
#if MODMONO_SERVER
|
||||
string filename;
|
||||
#endif
|
||||
|
||||
public XSPApplicationServer ()
|
||||
{
|
||||
#if !MODMONO_SERVER
|
||||
SetListenAddress (80);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MODMONO_SERVER
|
||||
public void SetListenFile (string filename)
|
||||
{
|
||||
if (filename == null)
|
||||
throw new ArgumentNullException ("filename");
|
||||
|
||||
this.filename = filename;
|
||||
}
|
||||
#else
|
||||
public void SetListenAddress (int port)
|
||||
{
|
||||
SetListenAddress (IPAddress.Any, port);
|
||||
|
@ -366,252 +39,151 @@ namespace Mono.ASPNET
|
|||
|
||||
public void SetListenAddress (IPEndPoint bindAddress)
|
||||
{
|
||||
if (started)
|
||||
throw new InvalidOperationException ("The server is already started.");
|
||||
|
||||
if (bindAddress == null)
|
||||
throw new ArgumentNullException ("bindAddress");
|
||||
|
||||
this.bindAddress = bindAddress;
|
||||
}
|
||||
#endif
|
||||
|
||||
public bool Verbose {
|
||||
get { return verbose; }
|
||||
set { verbose = value; }
|
||||
}
|
||||
|
||||
private void AddApplication (string vhost, int vport, string vpath, string fullPath)
|
||||
|
||||
public Socket CreateSocket ()
|
||||
{
|
||||
// TODO - check for duplicates, sort, optimize, etc.
|
||||
if (started)
|
||||
throw new InvalidOperationException ("The server is already started.");
|
||||
|
||||
if (verbose) {
|
||||
Console.WriteLine("Registering application:");
|
||||
Console.WriteLine(" Host: {0}", (vhost != null) ? vhost : "any");
|
||||
Console.WriteLine(" Port: {0}", (vport != -1) ?
|
||||
vport.ToString () : "any");
|
||||
|
||||
Console.WriteLine(" Virtual path: {0}", vpath);
|
||||
Console.WriteLine(" Physical path: {0}", fullPath);
|
||||
}
|
||||
|
||||
vpathToHost.Add (new VPathToHost (vhost, vport, vpath, fullPath));
|
||||
Socket listen_socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
|
||||
listen_socket.Bind (bindAddress);
|
||||
return listen_socket;
|
||||
}
|
||||
|
||||
public IWorker CreateWorker (Socket client, ApplicationServer server)
|
||||
{
|
||||
return new XSPWorker (client, client.LocalEndPoint, server);
|
||||
}
|
||||
|
||||
public void AddApplicationsFromConfigDirectory (string directoryName)
|
||||
{
|
||||
if (verbose) {
|
||||
Console.WriteLine ("Adding applications from *.webapp files in " +
|
||||
"directory '{0}'", directoryName);
|
||||
}
|
||||
|
||||
DirectoryInfo di = new DirectoryInfo (directoryName);
|
||||
foreach (FileInfo fi in di.GetFiles ("*.webapp")) {
|
||||
AddApplicationsFromConfigFile (fi.FullName);
|
||||
}
|
||||
|
||||
public Type GetApplicationHostType ()
|
||||
{
|
||||
return typeof (XSPApplicationHost);
|
||||
}
|
||||
|
||||
public IRequestBroker CreateRequestBroker ()
|
||||
{
|
||||
return new XSPRequestBroker ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// XSPRequestBroker: The request broker for XSP. Provides some
|
||||
// additional methods to the BaseRequestBroker from which inherits.
|
||||
//
|
||||
public class XSPRequestBroker: BaseRequestBroker
|
||||
{
|
||||
public bool IsConnected (int requestId)
|
||||
{
|
||||
return ((XSPWorker)GetWorker (requestId)).IsConnected ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// XSPApplicationHost: The application host for XSP.
|
||||
//
|
||||
public class XSPApplicationHost : BaseApplicationHost
|
||||
{
|
||||
public void ProcessRequest (int reqId, string localEPAddr, int localEPPort, string remoteEPAdds, int remoteEPPort, string verb, string path, string pathInfo, string queryString, string protocol, byte[] inputBuffer)
|
||||
{
|
||||
XSPRequestBroker broker = (XSPRequestBroker) RequestBroker;
|
||||
IPEndPoint localEP = new IPEndPoint (IPAddress.Parse(localEPAddr), localEPPort);
|
||||
IPEndPoint remoteEP = new IPEndPoint (IPAddress.Parse(remoteEPAdds), remoteEPPort);
|
||||
XSPWorkerRequest mwr = new XSPWorkerRequest (reqId, broker, this, localEP, remoteEP, verb, path, pathInfo, queryString, protocol, inputBuffer);
|
||||
ProcessRequest (mwr);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// XSPWorker: The worker that do the initial processing of XSP
|
||||
// requests.
|
||||
//
|
||||
internal class XSPWorker: IWorker
|
||||
{
|
||||
ApplicationServer server;
|
||||
LingeringNetworkStream stream;
|
||||
RequestData rdata;
|
||||
IPEndPoint remoteEP;
|
||||
IPEndPoint localEP;
|
||||
|
||||
public void AddApplicationsFromConfigFile (string fileName)
|
||||
{
|
||||
if (verbose) {
|
||||
Console.WriteLine ("Adding applications from config file '{0}'", fileName);
|
||||
}
|
||||
public XSPWorker (Socket client, EndPoint localEP, ApplicationServer server)
|
||||
{
|
||||
stream = new LingeringNetworkStream (client, true);
|
||||
this.server = server;
|
||||
|
||||
try {
|
||||
XmlDocument doc = new XmlDocument ();
|
||||
doc.Load (fileName);
|
||||
|
||||
foreach (XmlElement el in doc.SelectNodes ("//web-application")) {
|
||||
AddApplicationFromElement (el);
|
||||
}
|
||||
} catch {
|
||||
Console.WriteLine ("Error loading '{0}'", fileName);
|
||||
throw;
|
||||
}
|
||||
remoteEP = (IPEndPoint) client.RemoteEndPoint;
|
||||
} catch { }
|
||||
this.localEP = (IPEndPoint) localEP;
|
||||
}
|
||||
|
||||
void AddApplicationFromElement (XmlElement el)
|
||||
public void Run (object state)
|
||||
{
|
||||
XmlNode n;
|
||||
|
||||
string name = el.SelectSingleNode ("name").InnerText;
|
||||
string vpath = el.SelectSingleNode ("vpath").InnerText;
|
||||
string path = el.SelectSingleNode ("path").InnerText;
|
||||
|
||||
string vhost = null;
|
||||
n = el.SelectSingleNode ("vhost");
|
||||
#if !MOD_MONO_SERVER
|
||||
if (n != null)
|
||||
vhost = n.InnerText;
|
||||
#else
|
||||
// TODO: support vhosts in xsp.exe
|
||||
if (verbose)
|
||||
Console.WriteLine ("Ignoring vhost {0} for {1}", n.InnerText, name);
|
||||
#endif
|
||||
|
||||
int vport = -1;
|
||||
n = el.SelectSingleNode ("vport");
|
||||
#if !MOD_MONO_SERVER
|
||||
if (n != null)
|
||||
vport = Convert.ToInt32 (n.InnerText);
|
||||
#else
|
||||
// TODO: Listen on different ports
|
||||
if (verbose)
|
||||
Console.WriteLine ("Ignoring vport {0} for {1}", n.InnerText, name);
|
||||
#endif
|
||||
|
||||
AddApplication (vhost, vport, vpath, path);
|
||||
}
|
||||
|
||||
public void AddApplicationsFromCommandLine (string applications)
|
||||
{
|
||||
if (applications == null)
|
||||
throw new ArgumentNullException ("applications");
|
||||
|
||||
if (applications == "")
|
||||
return;
|
||||
|
||||
if (verbose) {
|
||||
Console.WriteLine("Adding applications '{0}'...", applications);
|
||||
}
|
||||
|
||||
string [] apps = applications.Split (',');
|
||||
|
||||
foreach (string str in apps) {
|
||||
string [] app = str.Split (':');
|
||||
|
||||
if (app.Length < 2 || app.Length > 4)
|
||||
throw new ArgumentException ("Should be something like " +
|
||||
"[[hostname:]port:]VPath:realpath");
|
||||
|
||||
int vport;
|
||||
string vhost;
|
||||
string vpath;
|
||||
string realpath;
|
||||
int pos = 0;
|
||||
|
||||
if (app.Length >= 3) {
|
||||
vhost = app[pos++];
|
||||
} else {
|
||||
vhost = null;
|
||||
}
|
||||
|
||||
if (app.Length >= 4) {
|
||||
// FIXME: support more than one listen port.
|
||||
vport = Convert.ToInt16 (app[pos++]);
|
||||
} else {
|
||||
vport = -1;
|
||||
}
|
||||
|
||||
vpath = app [pos++];
|
||||
realpath = app[pos++];
|
||||
|
||||
if (!vpath.EndsWith ("/"))
|
||||
vpath += "/";
|
||||
|
||||
string fullPath = System.IO.Path.GetFullPath (realpath);
|
||||
AddApplication (vhost, vport, vpath, fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Start (bool bgThread)
|
||||
{
|
||||
if (started)
|
||||
throw new InvalidOperationException ("The server is already started.");
|
||||
|
||||
if (vpathToHost == null)
|
||||
throw new InvalidOperationException ("SetApplications must be called first.");
|
||||
|
||||
if (vpathToHost.Count == 0)
|
||||
throw new InvalidOperationException ("No applications defined.");
|
||||
|
||||
#if MODMONO_SERVER
|
||||
if (filename == null)
|
||||
throw new InvalidOperationException ("filename not set");
|
||||
|
||||
File.Delete (filename);
|
||||
listen_socket = new Socket (AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
|
||||
EndPoint ep = new UnixEndPoint (filename);
|
||||
listen_socket.Bind (ep);
|
||||
#else
|
||||
listen_socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
|
||||
listen_socket.Bind (bindAddress);
|
||||
#endif
|
||||
listen_socket.Listen (5);
|
||||
runner = new Thread (new ThreadStart (RunServer));
|
||||
runner.IsBackground = bgThread;
|
||||
runner.Start ();
|
||||
stop = false;
|
||||
WebTrace.WriteLine ("Server started.");
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Stop ()
|
||||
{
|
||||
if (!started)
|
||||
throw new InvalidOperationException ("The server is not started.");
|
||||
|
||||
stop = true;
|
||||
listen_socket.Close ();
|
||||
lock (vpathToHost) {
|
||||
foreach (VPathToHost v in vpathToHost) {
|
||||
v.ClearHost ();
|
||||
}
|
||||
}
|
||||
WebTrace.WriteLine ("Server stopped.");
|
||||
}
|
||||
|
||||
private void RunServer ()
|
||||
{
|
||||
started = true;
|
||||
Socket client;
|
||||
while (!stop){
|
||||
client = listen_socket.Accept ();
|
||||
WebTrace.WriteLine ("Accepted connection.");
|
||||
Worker worker = new Worker (client, client.LocalEndPoint, this);
|
||||
ThreadPool.QueueUserWorkItem (new WaitCallback (worker.Run));
|
||||
}
|
||||
|
||||
started = false;
|
||||
}
|
||||
|
||||
public IApplicationHost GetApplicationForPath (string vhost, int port, string path,
|
||||
bool defaultToRoot)
|
||||
{
|
||||
VPathToHost bestMatch = null;
|
||||
int bestMatchLength = 0;
|
||||
|
||||
// Console.WriteLine("GetApplicationForPath({0},{1},{2},{3})", vhost, port,
|
||||
// path, defaultToRoot);
|
||||
for (int i = vpathToHost.Count - 1; i >= 0; i--) {
|
||||
VPathToHost v = (VPathToHost) vpathToHost [i];
|
||||
int matchLength = v.vpath.Length;
|
||||
if (matchLength <= bestMatchLength || !v.Match (vhost, port, path))
|
||||
continue;
|
||||
|
||||
bestMatchLength = matchLength;
|
||||
bestMatch = v;
|
||||
}
|
||||
|
||||
if (bestMatch != null) {
|
||||
lock (bestMatch) {
|
||||
if (bestMatch.appHost == null)
|
||||
bestMatch.CreateHost ();
|
||||
}
|
||||
|
||||
return bestMatch.appHost;
|
||||
}
|
||||
int requestId = -1;
|
||||
XSPRequestBroker broker = null;
|
||||
|
||||
if (defaultToRoot)
|
||||
return GetApplicationForPath (vhost, port, "/", false);
|
||||
try {
|
||||
if (remoteEP == null)
|
||||
return;
|
||||
|
||||
if (verbose)
|
||||
Console.WriteLine ("No application defined for: {0}:{1}{2}", vhost, port, path);
|
||||
InitialWorkerRequest ir = new InitialWorkerRequest (stream);
|
||||
ir.ReadRequestData ();
|
||||
RequestData rdata = ir.RequestData;
|
||||
string vhost = null; // TODO: read the headers in InitialWorkerRequest
|
||||
int port = ((IPEndPoint) localEP).Port;
|
||||
|
||||
VPathToHost vapp = server.GetApplicationForPath (vhost, port, rdata.Path, true);
|
||||
XSPApplicationHost host = (XSPApplicationHost) vapp.AppHost;
|
||||
if (host == null) {
|
||||
byte [] nf = HttpErrors.NotFound (rdata.Path);
|
||||
stream.Write (nf, 0, nf.Length);
|
||||
stream.Close ();
|
||||
return;
|
||||
}
|
||||
|
||||
broker = (XSPRequestBroker) vapp.RequestBroker;
|
||||
requestId = broker.RegisterRequest (this);
|
||||
|
||||
host.ProcessRequest (requestId, localEP.Address.ToString(), localEP.Port, remoteEP.Address.ToString(), remoteEP.Port, rdata.Verb, rdata.Path, rdata.PathInfo, rdata.QueryString, rdata.Protocol, rdata.InputBuffer);
|
||||
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine (e);
|
||||
try {
|
||||
byte [] error = HttpErrors.ServerError ();
|
||||
stream.Write (error, 0, error.Length);
|
||||
stream.Close ();
|
||||
} catch {}
|
||||
}
|
||||
finally {
|
||||
if (requestId != -1)
|
||||
broker.UnregisterRequest (requestId);
|
||||
}
|
||||
}
|
||||
|
||||
public int Read (byte[] buffer, int position, int size)
|
||||
{
|
||||
return stream.Read (buffer, position, size);
|
||||
}
|
||||
|
||||
public void Write (byte[] buffer, int position, int size)
|
||||
{
|
||||
stream.Write (buffer, position, size);
|
||||
}
|
||||
|
||||
public void Close ()
|
||||
{
|
||||
stream.Close ();
|
||||
}
|
||||
|
||||
public bool IsConnected ()
|
||||
{
|
||||
return stream.Connected;
|
||||
}
|
||||
|
||||
public void Flush ()
|
||||
{
|
||||
stream.Flush ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ namespace Mono.ASPNET
|
|||
public class XSPWorkerRequest : MonoWorkerRequest
|
||||
{
|
||||
IApplicationHost appHost;
|
||||
MyNetworkStream stream;
|
||||
string verb;
|
||||
string path;
|
||||
string pathInfo;
|
||||
|
@ -45,6 +44,8 @@ namespace Mono.ASPNET
|
|||
bool sentConnection;
|
||||
int localPort;
|
||||
string localAddress;
|
||||
int requestId;
|
||||
XSPRequestBroker requestBroker;
|
||||
|
||||
static byte [] error500;
|
||||
|
||||
|
@ -105,26 +106,33 @@ namespace Mono.ASPNET
|
|||
indexFiles = (string []) files.ToArray (typeof (string));
|
||||
}
|
||||
|
||||
public XSPWorkerRequest (MyNetworkStream ns, IApplicationHost appHost, EndPoint localEP,
|
||||
EndPoint remoteEP, RequestData rdata)
|
||||
public XSPWorkerRequest (int requestId,
|
||||
XSPRequestBroker requestBroker,
|
||||
IApplicationHost appHost,
|
||||
EndPoint localEP,
|
||||
EndPoint remoteEP,
|
||||
string verb,
|
||||
string path,
|
||||
string pathInfo,
|
||||
string queryString,
|
||||
string protocol,
|
||||
byte[] inputBuffer)
|
||||
: base (appHost)
|
||||
{
|
||||
if (ns == null)
|
||||
throw new ArgumentNullException ("ns");
|
||||
|
||||
this.requestId = requestId;
|
||||
this.requestBroker = requestBroker;
|
||||
this.appHost = appHost;
|
||||
this.localEP = localEP;
|
||||
this.remoteEP = remoteEP;
|
||||
stream = ns;
|
||||
verb = rdata.Verb;
|
||||
path = rdata.Path;
|
||||
pathInfo = rdata.PathInfo;
|
||||
protocol = rdata.Protocol;
|
||||
this.verb = verb;
|
||||
this.path = path;
|
||||
this.pathInfo = pathInfo;
|
||||
this.protocol = protocol;
|
||||
if (protocol == "HTTP/1.1")
|
||||
protocol = "HTTP/1.0"; // Only 1.0 supported by xsp standalone.
|
||||
|
||||
queryString = rdata.QueryString;
|
||||
inputBuffer = rdata.InputBuffer;
|
||||
this.queryString = queryString;
|
||||
this.inputBuffer = inputBuffer;
|
||||
inputLength = inputBuffer.Length;
|
||||
position = 0;
|
||||
|
||||
|
@ -136,11 +144,15 @@ namespace Mono.ASPNET
|
|||
localPort = ((IPEndPoint) localEP).Port;
|
||||
localAddress = ((IPEndPoint) localEP).Address.ToString();
|
||||
}
|
||||
|
||||
public int RequestId
|
||||
{
|
||||
get { return requestId; }
|
||||
}
|
||||
|
||||
void FillBuffer ()
|
||||
{
|
||||
inputBuffer = new byte [32*1024];
|
||||
inputLength = stream.Read (inputBuffer, 0, 32*1024);
|
||||
inputLength = requestBroker.Read (requestId, 32*1024, out inputBuffer);
|
||||
position = 0;
|
||||
}
|
||||
|
||||
|
@ -212,9 +224,9 @@ namespace Mono.ASPNET
|
|||
public override void CloseConnection ()
|
||||
{
|
||||
WebTrace.WriteLine ("CloseConnection()");
|
||||
if (stream != null) {
|
||||
stream.Close ();
|
||||
stream = null;
|
||||
if (requestBroker != null) {
|
||||
requestBroker.Close (requestId);
|
||||
requestBroker = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,10 +246,10 @@ namespace Mono.ASPNET
|
|||
|
||||
if (response.Length != 0) {
|
||||
byte [] bytes = response.GetBuffer ();
|
||||
stream.Write (bytes, 0, (int) response.Length);
|
||||
requestBroker.Write (requestId, bytes, 0, (int) response.Length);
|
||||
}
|
||||
|
||||
stream.Flush ();
|
||||
|
||||
requestBroker.Flush (requestId);
|
||||
response.SetLength (0);
|
||||
if (finalFlush)
|
||||
CloseConnection ();
|
||||
|
@ -419,7 +431,7 @@ namespace Mono.ASPNET
|
|||
public override bool IsClientConnected ()
|
||||
{
|
||||
WebTrace.WriteLine ("IsClientConnected()");
|
||||
return stream.Connected;
|
||||
return requestBroker.IsConnected (requestId);
|
||||
}
|
||||
|
||||
public override bool IsEntireEntityBodyIsPreloaded ()
|
||||
|
@ -457,7 +469,7 @@ namespace Mono.ASPNET
|
|||
void WriteString (string s)
|
||||
{
|
||||
byte [] b = Encoding.GetBytes (s);
|
||||
stream.Write (b, 0, b.Length);
|
||||
requestBroker.Write (requestId, b, 0, b.Length);
|
||||
|
||||
}
|
||||
|
||||
|
@ -483,7 +495,9 @@ namespace Mono.ASPNET
|
|||
|
||||
int localsize = size;
|
||||
while (localsize > 0) {
|
||||
int read = stream.Read (buffer, offset, localsize);
|
||||
byte[] readBuffer;
|
||||
int read = requestBroker.Read (requestId, localsize, out readBuffer);
|
||||
Array.Copy (readBuffer, 0, buffer, offset, read);
|
||||
offset += read;
|
||||
localsize -= read;
|
||||
}
|
||||
|
|
|
@ -242,7 +242,13 @@ namespace Mono.ASPNET
|
|||
|
||||
rootDir = Directory.GetCurrentDirectory ();
|
||||
|
||||
XSPApplicationServer server = new XSPApplicationServer ();
|
||||
#if MODMONO_SERVER
|
||||
ModMonoWebSource webSource = new ModMonoWebSource ();
|
||||
ApplicationServer server = new ApplicationServer (webSource);
|
||||
#else
|
||||
XSPWebSource webSource = new XSPWebSource ();
|
||||
ApplicationServer server = new ApplicationServer (webSource);
|
||||
#endif
|
||||
server.Verbose = verbose;
|
||||
|
||||
if (apps != null)
|
||||
|
@ -257,10 +263,10 @@ namespace Mono.ASPNET
|
|||
if (apps == null && appConfigDir == null && appConfigFile == null)
|
||||
server.AddApplicationsFromCommandLine ("/:.");
|
||||
#if MODMONO_SERVER
|
||||
server.SetListenFile (filename);
|
||||
webSource.SetListenFile (filename);
|
||||
Console.WriteLine ("Listening on: {0}", filename);
|
||||
#else
|
||||
server.SetListenAddress (IPAddress.Parse (ip), port);
|
||||
webSource.SetListenAddress (IPAddress.Parse (ip), port);
|
||||
Console.WriteLine ("Listening on port: {0}", port);
|
||||
Console.WriteLine ("Listening on address: {0}", ip);
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче