* 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:
Lluis Sanchez 2004-06-20 21:27:24 +00:00
Родитель ac054ca27b
Коммит f997661c65
14 изменённых файлов: 1215 добавлений и 644 удалений

Просмотреть файл

@ -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

419
server/ApplicationServer.cs Normal file
Просмотреть файл

@ -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
{
}
}

33
server/IWebSource.cs Normal file
Просмотреть файл

@ -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