Dont index style nodes.
[beagle.git] / beagled / WebServices / WebServer / XSPWorkerRequest.cs
blobd5f566f5863b70571b908ac70ad378c030543a18
1 //
2 // Mono.ASPNET.XSPWorkerRequest
3 //
4 // Authors:
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 // Simon Waite (simon@psionics.demon.co.uk)
7 //
8 // (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
9 // (C) Copyright 2004 Novell, Inc. (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System;
32 using System.Collections;
33 using System.Configuration;
34 using System.Diagnostics;
35 using System.Globalization;
36 using System.IO;
37 using System.Net;
38 using System.Net.Sockets;
39 using System.Reflection;
40 using System.Text;
41 using System.Threading;
42 using System.Web;
43 using System.Web.Hosting;
45 namespace Mono.ASPNET
47 public class XSPWorkerRequest : MonoWorkerRequest
49 string verb;
50 string path;
51 string pathInfo;
52 string queryString;
53 string protocol;
54 Hashtable headers;
55 string [][] unknownHeaders;
56 bool headersSent;
57 StringBuilder responseHeaders;
58 string status;
59 MemoryStream response;
60 byte [] inputBuffer;
61 int inputLength;
62 int position;
63 EndPoint remoteEP;
64 bool sentConnection;
65 int localPort;
66 string localAddress;
67 int requestId;
68 XSPRequestBroker requestBroker;
69 bool keepAlive;
70 bool haveContentLength;
71 long contentSent;
72 long contentLength;
74 static string server_software;
75 static string serverHeader;
77 static string [] indexFiles = { "index.aspx",
78 "Default.aspx",
79 "default.aspx",
80 "index.html",
81 "index.htm" };
84 static XSPWorkerRequest ()
86 Assembly assembly = Assembly.GetExecutingAssembly ();
87 string title = "Beagle-XSP Server";
88 string version = assembly.GetName ().Version.ToString ();
89 object [] att = assembly.GetCustomAttributes (typeof (AssemblyTitleAttribute), false);
90 if (att.Length > 0)
91 title = ((AssemblyTitleAttribute) att [0]).Title;
93 string plat = Environment.OSVersion.Platform.ToString ();
94 if (plat == "128")
95 plat = "Unix";
97 server_software = String.Format ("{0}/{1}", title, version);
98 serverHeader = String.Format ("Server: {0} {1}\r\n", server_software, plat);
100 string indexes = ConfigurationSettings.AppSettings ["MonoServerDefaultIndexFiles"];
101 SetDefaultIndexFiles (indexes);
104 static void SetDefaultIndexFiles (string list)
106 if (list == null)
107 return;
109 ArrayList files = new ArrayList ();
110 string [] fs = list.Split (',');
111 foreach (string f in fs) {
112 string trimmed = f.Trim ();
113 if (trimmed == "")
114 continue;
116 files.Add (trimmed);
119 indexFiles = (string []) files.ToArray (typeof (string));
122 static Stack bufferStack = new Stack ();
124 static MemoryStream AllocateMemoryStream ()
126 lock (bufferStack) {
127 if (bufferStack.Count != 0)
128 return (MemoryStream) bufferStack.Pop ();
130 return new MemoryStream ();
133 static void FreeMemoryStream (MemoryStream buf)
135 buf.SetLength (0);
136 lock (bufferStack) {
137 bufferStack.Push (buf);
141 public XSPWorkerRequest (int requestId,
142 XSPRequestBroker requestBroker,
143 IApplicationHost appHost,
144 EndPoint localEP,
145 EndPoint remoteEP,
146 string verb,
147 string path,
148 string queryString,
149 string protocol,
150 byte[] inputBuffer)
151 : base (appHost)
153 this.requestId = requestId;
154 this.requestBroker = requestBroker;
155 this.remoteEP = remoteEP;
156 this.verb = verb;
157 Paths.GetPathsFromUri (path, out this.path, out pathInfo);
158 this.protocol = protocol;
159 if (protocol == "HTTP/1.1") {
160 this.protocol = "HTTP/1.0"; // Only 1.0 supported by xsp standalone.
161 keepAlive = true;
164 this.queryString = queryString;
165 this.inputBuffer = inputBuffer;
166 inputLength = inputBuffer.Length;
167 position = 0;
169 GetRequestHeaders ();
170 string cncHeader = (string) headers ["Connection"];
171 if (cncHeader != null) {
172 cncHeader = cncHeader.ToLower ();
173 if (cncHeader.IndexOf ("keep-alive") != -1)
174 keepAlive = true;
176 if (cncHeader.IndexOf ("close") != -1)
177 keepAlive = false;
180 responseHeaders = new StringBuilder ();
181 responseHeaders.Append (serverHeader);
182 response = AllocateMemoryStream ();
183 status = "HTTP/1.0 200 OK\r\n";
185 localPort = ((IPEndPoint) localEP).Port;
186 localAddress = ((IPEndPoint) localEP).Address.ToString();
189 public override int RequestId {
190 get { return requestId; }
193 void FillBuffer ()
195 inputLength = requestBroker.Read (requestId, 32*1024, out inputBuffer);
196 position = 0;
199 int ReadInputByte ()
201 if (inputBuffer == null || position >= inputLength)
202 FillBuffer ();
204 return (int) inputBuffer [position++];
207 string ReadLine ()
209 bool foundCR = false;
210 StringBuilder text = new StringBuilder ();
212 while (true) {
213 int c = ReadInputByte ();
215 if (c == -1) { // end of stream
216 if (text.Length == 0)
217 return null;
219 if (foundCR)
220 text.Length--;
222 break;
225 if (c == '\n') { // newline
226 if ((text.Length > 0) && (text [text.Length - 1] == '\r'))
227 text.Length--;
229 foundCR = false;
230 break;
231 } else if (foundCR) {
232 text.Length--;
233 break;
236 if (c == '\r')
237 foundCR = true;
240 text.Append ((char) c);
241 if (text.Length > 8192)
242 throw new InvalidOperationException ("Line too long.");
245 return text.ToString ();
248 void GetRequestHeaders ()
250 try {
251 string line;
252 headers = new Hashtable (CaseInsensitiveHashCodeProvider.DefaultInvariant,
253 CaseInsensitiveComparer.DefaultInvariant);
254 while ((line = ReadLine ()) != null && line.Length > 0) {
255 int colon = line.IndexOf (':');
256 if (colon == -1 || line.Length < colon + 2)
257 throw new Exception ();
258 string key = line.Substring (0, colon);
259 string value = line.Substring (colon + 1).Trim ();
260 headers [key] = value;
262 } catch (IOException ioe) {
263 throw;
264 } catch (Exception e) {
265 throw new Exception ("Error reading headers.", e);
269 public override void CloseConnection ()
271 WebTrace.WriteLine ("CloseConnection()");
272 if (requestBroker != null) {
273 // We check for headersSent as broken user code might call
274 // CloseConnection at an early stage.
275 requestBroker.Close (requestId, (headersSent ? keepAlive : false));
276 requestBroker = null;
277 FreeMemoryStream (response);
278 response = null;
282 void AddConnectionHeader ()
284 if (!keepAlive) {
285 responseHeaders.Append ("Connection: close\r\n");
286 return;
289 int allowed = requestBroker.GetReuseCount (requestId);
290 if (allowed <= 0) {
291 keepAlive = false;
292 responseHeaders.Append ("Connection: close\r\n");
293 return;
296 responseHeaders.Append ("Keep-Alive: timeout=15, max=");
297 responseHeaders.Append (allowed.ToString ());
298 responseHeaders.Append ("\r\n");
299 responseHeaders.Append ("Connection: Keep-Alive\r\n");
302 int UpdateBodyLength (int currentBlockLength)
304 if (!haveContentLength || contentSent < contentLength - currentBlockLength) {
305 contentSent += currentBlockLength;
306 return currentBlockLength;
309 int result = (int) (contentLength - contentSent);
310 contentSent = contentLength;
311 return result;
314 public override void FlushResponse (bool finalFlush)
316 if (requestBroker == null)
317 return;
319 try {
320 if (!headersSent) {
321 responseHeaders.Insert (0, status);
322 if (!sentConnection) {
323 if (!haveContentLength)
324 keepAlive = false;
326 AddConnectionHeader ();
329 responseHeaders.Append ("\r\n");
330 byte [] headerBytes = Encoding.GetBytes (responseHeaders.ToString ());
331 int oldLength = (int) response.Length;
332 if (oldLength == 0 || oldLength >= 32768) {
333 requestBroker.Write (requestId, headerBytes, 0, headerBytes.Length);
334 } else {
335 oldLength = UpdateBodyLength (oldLength);
336 // Attempt not to send a minimum of 2 packets
337 int newLength = oldLength + headerBytes.Length;
338 response.SetLength (newLength);
339 byte [] buf = response.GetBuffer ();
340 Buffer.BlockCopy (buf, 0, buf, headerBytes.Length, oldLength);
341 Buffer.BlockCopy (headerBytes, 0, buf, 0, headerBytes.Length);
342 requestBroker.Write (requestId, buf, 0, newLength);
343 response.SetLength (0);
346 headersSent = true;
349 if (response.Length != 0) {
350 byte [] bytes = response.GetBuffer ();
351 int len = UpdateBodyLength ((int) response.Length);
352 requestBroker.Write (requestId, bytes, 0, len);
355 if (finalFlush)
356 CloseConnection ();
357 else {
358 requestBroker.Flush (requestId);
359 response.SetLength (0);
361 } catch (Exception e) {
362 WebTrace.WriteLine (e.ToString ());
363 CloseConnection ();
367 public override string GetFilePath ()
369 WebTrace.WriteLine ("GetFilePath()");
370 return path;
373 public override string GetHttpVerbName ()
375 WebTrace.WriteLine ("GetHttpVerbName()");
376 return verb;
379 public override string GetHttpVersion ()
381 WebTrace.WriteLine ("GetHttpVersion()");
382 return protocol;
385 public override string GetKnownRequestHeader (int index)
387 if (headers == null)
388 return null;
390 string headerName = HttpWorkerRequest.GetKnownRequestHeaderName (index);
391 WebTrace.WriteLine (String.Format ("GetKnownRequestHeader({0}) -> {1}", index, headerName));
392 return headers [headerName] as string;
395 public override string GetUnknownRequestHeader (string name)
397 if (headers == null)
398 return null;
400 return headers [name] as string;
403 public override string [][] GetUnknownRequestHeaders ()
405 if (unknownHeaders == null) {
406 if (headers == null)
407 return (unknownHeaders = new string [0][]);
409 ICollection keysColl = headers.Keys;
410 ICollection valuesColl = headers.Values;
411 string [] keys = new string [keysColl.Count];
412 string [] values = new string [valuesColl.Count];
413 keysColl.CopyTo (keys, 0);
414 valuesColl.CopyTo (values, 0);
416 int count = keys.Length;
417 ArrayList pairs = new ArrayList ();
418 for (int i = 0; i < count; i++) {
419 int index = HttpWorkerRequest.GetKnownRequestHeaderIndex (keys [i]);
420 if (index != -1)
421 continue;
422 pairs.Add (new string [] { keys [i], values [i]});
425 if (pairs.Count != 0) {
426 unknownHeaders = new string [pairs.Count][];
427 for (int i = 0; i < pairs.Count; i++)
428 unknownHeaders [i] = (string []) pairs [i];
429 //unknownHeaders = (string [][]) pairs.ToArray (typeof (string [][]));
433 return unknownHeaders;
436 public override string GetLocalAddress ()
438 WebTrace.WriteLine ("GetLocalAddress()");
439 return localAddress;
442 public override int GetLocalPort ()
444 WebTrace.WriteLine ("GetLocalPort()");
445 return localPort;
448 public override string GetPathInfo ()
450 WebTrace.WriteLine ("GetPathInfo()");
451 return pathInfo;
454 public override byte [] GetPreloadedEntityBody ()
456 WebTrace.WriteLine ("GetPreloadedEntityBody");
457 return null;
460 public override string GetQueryString ()
462 WebTrace.WriteLine ("GetQueryString()");
463 return queryString;
466 public override byte [] GetQueryStringRawBytes ()
468 WebTrace.WriteLine ("GetQueryStringRawBytes()");
469 if (queryString == null)
470 return null;
471 return Encoding.GetBytes (queryString);
474 public override string GetRawUrl ()
476 WebTrace.WriteLine ("GetRawUrl()");
477 string result = path;
478 if (pathInfo != null && pathInfo.Length > 0)
479 result += pathInfo;
481 if (queryString != null && queryString.Length > 0)
482 return result + "?" + queryString;
484 return result;
487 public override string GetRemoteAddress ()
489 WebTrace.WriteLine ("GetRemoteAddress()");
490 return ((IPEndPoint) remoteEP).Address.ToString ();
493 public override string GetRemoteName ()
495 string ip = GetRemoteAddress ();
496 string name = null;
497 try {
498 IPHostEntry entry = Dns.GetHostByName (ip);
499 name = entry.HostName;
500 } catch {
501 name = ip;
504 return name;
507 public override int GetRemotePort ()
509 WebTrace.WriteLine ("GetRemotePort()");
510 return ((IPEndPoint) remoteEP).Port;
514 public override string GetServerVariable (string name)
516 string result = null;
517 switch (name) {
518 case "GATEWAY_INTERFACE":
519 result = "CGI/1.1";
520 break;
521 case "HTTPS":
522 result = (IsSecure ()) ? "on" : "off";
523 break;
524 case "SERVER_SOFTWARE":
525 result = server_software;
526 break;
527 default:
528 result = base.GetServerVariable (name);
529 break;
532 return result;
535 public override string GetUriPath ()
537 WebTrace.WriteLine ("GetUriPath()");
539 string result = path;
540 if (pathInfo != null && pathInfo.Length > 0)
541 result += pathInfo;
543 return result;
546 public override bool HeadersSent ()
548 WebTrace.WriteLine ("HeadersSent() -> " + headersSent);
549 return headersSent;
552 public override bool IsClientConnected ()
554 WebTrace.WriteLine ("IsClientConnected()");
555 return (requestBroker != null && requestBroker.IsConnected (requestId));
558 public override bool IsEntireEntityBodyIsPreloaded ()
560 return false; //TODO: handle preloading data
563 bool TryDirectory ()
565 string localPath = GetFilePathTranslated ();
567 if (!Directory.Exists (localPath))
568 return true;
570 string oldPath = path;
571 if (!path.EndsWith ("/"))
572 path += "/";
574 bool catOne = false;
575 foreach (string indexFile in indexFiles) {
576 string testfile = Path.Combine (localPath, indexFile);
577 if (File.Exists (testfile)) {
578 path += indexFile;
579 catOne = true;
580 break;
584 if (!catOne)
585 path = oldPath;
587 return true;
590 protected override bool GetRequestData ()
592 return TryDirectory ();
595 int ReadInput (byte [] buffer, int offset, int size)
597 int length = inputLength - position;
598 if (length > 0) {
599 if (length > size)
600 length = size;
602 Buffer.BlockCopy (inputBuffer, position, buffer, offset, length);
603 position += length;
604 offset += length;
605 size -= length;
606 if (size == 0)
607 return length;
610 int localsize = size;
611 while (localsize > 0) {
612 byte[] readBuffer;
613 int read = requestBroker.Read (requestId, localsize, out readBuffer);
614 Array.Copy (readBuffer, 0, buffer, offset, read);
615 offset += read;
616 localsize -= read;
619 return (length + size);
622 public override int ReadEntityBody (byte [] buffer, int size)
624 WebTrace.WriteLine ("ReadEntityBody()");
625 if (size == 0)
626 return 0;
628 return ReadInput (buffer, 0, size);
631 public override void SendResponseFromMemory (byte [] data, int length)
633 WebTrace.WriteLine ("SendResponseFromMemory ()");
634 if (requestBroker == null || length <= 0)
635 return;
637 if (data.Length < length)
638 length = data.Length;
640 response.Write (data, 0, length);
643 public override void SendStatus (int statusCode, string statusDescription)
645 status = String.Format ("HTTP/1.0 {0} {1}\r\n", statusCode, statusDescription);
648 public override void SendUnknownResponseHeader (string name, string value)
650 WebTrace.WriteLine ("SendUnknownResponseHeader (" + name + ", " + value + ")");
651 if (String.Compare (name, "connection", true, CultureInfo.InvariantCulture) == 0) {
652 sentConnection = true;
653 if (value.ToLower ().IndexOf ("keep-alive") == -1) {
654 keepAlive = false;
658 if (!sentConnection && !haveContentLength &&
659 String.Compare (name, "Content-Length", true, CultureInfo.InvariantCulture) == 0) {
660 haveContentLength = true;
661 contentLength = Int64.Parse (value); // This should work, otherwise HttpResponse throws.
664 if (!headersSent) {
665 responseHeaders.Append (name);
666 responseHeaders.Append (": ");
667 responseHeaders.Append (value);
668 responseHeaders.Append ("\r\n");