2 // Mono.ASPNET.XSPApplicationHost
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 // Lluis Sanchez Gual (lluis@ximian.com)
8 // (C) Copyright 2004 Novell, Inc. (http://www.novell.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System
.Net
.Sockets
;
40 // XSPWebSource: Provides methods to get objects and types specific
43 public class XSPWebSource
: IWebSource
45 IPEndPoint bindAddress
;
47 public XSPWebSource (IPAddress address
, int port
)
49 SetListenAddress (address
, port
);
52 public void SetListenAddress (int port
)
54 SetListenAddress (IPAddress
.Any
, port
);
57 public void SetListenAddress (IPAddress address
, int port
)
59 SetListenAddress (new IPEndPoint (address
, port
));
62 public void SetListenAddress (IPEndPoint bindAddress
)
64 if (bindAddress
== null)
65 throw new ArgumentNullException ("bindAddress");
67 this.bindAddress
= bindAddress
;
70 public Socket
CreateSocket ()
72 Socket listen_socket
= new Socket (AddressFamily
.InterNetwork
, SocketType
.Stream
, ProtocolType
.IP
);
73 listen_socket
.Bind (bindAddress
);
77 public IWorker
CreateWorker (Socket client
, ApplicationServer server
)
79 return new XSPWorker (client
, client
.LocalEndPoint
, server
);
82 public Type
GetApplicationHostType ()
84 return typeof (XSPApplicationHost
);
87 public IRequestBroker
CreateRequestBroker ()
89 return new XSPRequestBroker ();
92 public void Dispose ()
98 // XSPRequestBroker: The request broker for XSP. Provides some
99 // additional methods to the BaseRequestBroker from which inherits.
101 public class XSPRequestBroker
: BaseRequestBroker
103 public bool IsConnected (int requestId
)
105 return ((XSPWorker
)GetWorker (requestId
)).IsConnected ();
108 public int GetReuseCount (int requestId
)
110 XSPWorker worker
= (XSPWorker
) GetWorker (requestId
);
111 return worker
.GetReuseCount ();
114 public void Close (int requestId
, bool keepAlive
)
116 XSPWorker worker
= (XSPWorker
) GetWorker (requestId
);
117 if (worker
!= null) {
119 worker
.Close (keepAlive
);
126 // XSPApplicationHost: The application host for XSP.
128 public class XSPApplicationHost
: BaseApplicationHost
130 public void ProcessRequest (int reqId
, long localEPAddr
, int localEPPort
, long remoteEPAdds
,
131 int remoteEPPort
, string verb
, string path
,
132 string queryString
, string protocol
, byte [] inputBuffer
, string redirect
)
134 XSPRequestBroker broker
= (XSPRequestBroker
) RequestBroker
;
135 IPEndPoint localEP
= new IPEndPoint (localEPAddr
, localEPPort
);
136 IPEndPoint remoteEP
= new IPEndPoint (remoteEPAdds
, remoteEPPort
);
137 XSPWorkerRequest mwr
= new XSPWorkerRequest (reqId
, broker
, this, localEP
, remoteEP
, verb
, path
,
138 queryString
, protocol
, inputBuffer
);
140 string translated
= mwr
.GetFilePathTranslated ();
141 if (path
[path
.Length
- 1] != '/' && Directory
.Exists (translated
))
142 redirect
= path
+ '/';
144 if (redirect
!= null) {
145 Redirect (mwr
, redirect
);
146 broker
.UnregisterRequest (reqId
);
150 ProcessRequest (mwr
);
153 static string content301
= "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" +
154 "<html><head>\n<title>301 Moved Permanently</title>\n</head><body>\n" +
155 "<h1>Moved Permanently</h1>\n" +
156 "<p>The document has moved to <a href='http://{0}{1}'>http://{0}{1}</a>.</p>\n" +
159 static void Redirect (XSPWorkerRequest wr
, string location
)
161 string host
= wr
.GetKnownRequestHeader (HttpWorkerRequest
.HeaderHost
);
162 wr
.SendStatus (301, "Moved Permanently");
163 wr
.SendUnknownResponseHeader ("Connection", "close");
164 wr
.SendUnknownResponseHeader ("Date", DateTime
.Now
.ToUniversalTime ().ToString ("r"));
165 wr
.SendUnknownResponseHeader ("Location", String
.Format ("http://{0}{1}", host
, location
));
166 Encoding enc
= Encoding
.ASCII
;
167 wr
.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc
.WebName
);
168 string content
= String
.Format (content301
, host
, location
);
169 byte [] contentBytes
= enc
.GetBytes (content
);
170 wr
.SendUnknownResponseHeader ("Content-Length", contentBytes
.Length
.ToString ());
171 wr
.SendResponseFromMemory (contentBytes
, contentBytes
.Length
);
172 wr
.FlushResponse (true);
173 wr
.CloseConnection ();
178 // XSPWorker: The worker that do the initial processing of XSP
181 internal class XSPWorker
: IWorker
183 ApplicationServer server
;
184 LingeringNetworkStream stream
;
189 public XSPWorker (Socket client
, EndPoint localEP
, ApplicationServer server
)
191 stream
= new LingeringNetworkStream (client
, false);
193 this.server
= server
;
196 remoteEP
= (IPEndPoint
) client
.RemoteEndPoint
;
198 this.localEP
= (IPEndPoint
) localEP
;
201 public int GetReuseCount ()
203 return server
.GetAvailableReuses (sock
);
206 public void Run (object state
)
209 XSPRequestBroker broker
= null;
212 if (remoteEP
== null)
215 InitialWorkerRequest ir
= new InitialWorkerRequest (stream
);
217 ir
.ReadRequestData ();
218 } catch (Exception ex
) {
219 if (ir
.GotSomeInput
) {
220 byte [] badReq
= HttpErrors
.BadRequest ();
221 Write (badReq
, 0, badReq
.Length
);
227 RequestData rdata
= ir
.RequestData
;
228 string vhost
= null; // TODO: read the headers in InitialWorkerRequest
229 int port
= ((IPEndPoint
) localEP
).Port
;
231 VPathToHost vapp
= server
.GetApplicationForPath (vhost
, port
, rdata
.Path
, true);
232 XSPApplicationHost host
= null;
234 host
= (XSPApplicationHost
) vapp
.AppHost
;
237 byte [] nf
= HttpErrors
.NotFound (rdata
.Path
);
238 Write (nf
, 0, nf
.Length
);
243 broker
= (XSPRequestBroker
) vapp
.RequestBroker
;
244 requestId
= broker
.RegisterRequest (this);
247 vapp
.Redirect (rdata
.Path
, out redirect
);
248 host
.ProcessRequest (requestId
, localEP
.Address
.Address
, localEP
.Port
,
249 remoteEP
.Address
.Address
, remoteEP
.Port
, rdata
.Verb
,
250 rdata
.Path
, rdata
.QueryString
,
251 rdata
.Protocol
, rdata
.InputBuffer
, redirect
);
252 } catch (Exception e
) {
253 bool ignore
= ((e
is RequestLineException
) || (e
is IOException
));
255 Console
.WriteLine (e
);
259 byte [] error
= HttpErrors
.ServerError ();
260 Write (error
, 0, error
.Length
);
267 if (broker
!= null && requestId
!= -1)
268 broker
.UnregisterRequest (requestId
);
272 public int Read (byte[] buffer
, int position
, int size
)
274 return stream
.Read (buffer
, position
, size
);
277 public void Write (byte[] buffer
, int position
, int size
)
280 stream
.Write (buffer
, position
, size
);
281 } catch (Exception e
) {
292 public void Close (bool keepAlive
)
294 if (!keepAlive
|| !IsConnected ()) {
296 server
.CloseSocket (sock
);
300 stream
.EnableLingering
= false;
302 server
.ReuseSocket (sock
);
305 public bool IsConnected ()
307 return stream
.Connected
;