2 // Mono.ASPNET.XSPWorkerRequest
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 // Simon Waite (simon@psionics.demon.co.uk)
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
32 using System
.Collections
;
33 using System
.Configuration
;
34 using System
.Diagnostics
;
35 using System
.Globalization
;
38 using System
.Net
.Sockets
;
39 using System
.Reflection
;
41 using System
.Threading
;
43 using System
.Web
.Hosting
;
47 public class XSPWorkerRequest
: MonoWorkerRequest
55 string [][] unknownHeaders
;
57 StringBuilder responseHeaders
;
59 MemoryStream response
;
68 XSPRequestBroker requestBroker
;
70 bool haveContentLength
;
74 static string server_software
;
75 static string serverHeader
;
77 static string [] indexFiles
= { "index.aspx",
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);
91 title
= ((AssemblyTitleAttribute
) att
[0]).Title
;
93 string plat
= Environment
.OSVersion
.Platform
.ToString ();
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
)
109 ArrayList files
= new ArrayList ();
110 string [] fs
= list
.Split (',');
111 foreach (string f
in fs
) {
112 string trimmed
= f
.Trim ();
119 indexFiles
= (string []) files
.ToArray (typeof (string));
122 static Stack bufferStack
= new Stack ();
124 static MemoryStream
AllocateMemoryStream ()
127 if (bufferStack
.Count
!= 0)
128 return (MemoryStream
) bufferStack
.Pop ();
130 return new MemoryStream ();
133 static void FreeMemoryStream (MemoryStream buf
)
137 bufferStack
.Push (buf
);
141 public XSPWorkerRequest (int requestId
,
142 XSPRequestBroker requestBroker
,
143 IApplicationHost appHost
,
153 this.requestId
= requestId
;
154 this.requestBroker
= requestBroker
;
155 this.remoteEP
= remoteEP
;
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.
164 this.queryString
= queryString
;
165 this.inputBuffer
= inputBuffer
;
166 inputLength
= inputBuffer
.Length
;
169 GetRequestHeaders ();
170 string cncHeader
= (string) headers
["Connection"];
171 if (cncHeader
!= null) {
172 cncHeader
= cncHeader
.ToLower ();
173 if (cncHeader
.IndexOf ("keep-alive") != -1)
176 if (cncHeader
.IndexOf ("close") != -1)
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; }
195 inputLength
= requestBroker
.Read (requestId
, 32*1024, out inputBuffer
);
201 if (inputBuffer
== null || position
>= inputLength
)
204 return (int) inputBuffer
[position
++];
209 bool foundCR
= false;
210 StringBuilder text
= new StringBuilder ();
213 int c
= ReadInputByte ();
215 if (c
== -1) { // end of stream
216 if (text
.Length
== 0)
225 if (c
== '\n') { // newline
226 if ((text
.Length
> 0) && (text
[text
.Length
- 1] == '\r'))
231 } else if (foundCR
) {
240 text
.Append ((char) c
);
241 if (text
.Length
> 8192)
242 throw new InvalidOperationException ("Line too long.");
245 return text
.ToString ();
248 void GetRequestHeaders ()
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
) {
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
);
282 void AddConnectionHeader ()
285 responseHeaders
.Append ("Connection: close\r\n");
289 int allowed
= requestBroker
.GetReuseCount (requestId
);
292 responseHeaders
.Append ("Connection: close\r\n");
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
;
314 public override void FlushResponse (bool finalFlush
)
316 if (requestBroker
== null)
321 responseHeaders
.Insert (0, status
);
322 if (!sentConnection
) {
323 if (!haveContentLength
)
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
);
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);
349 if (response
.Length
!= 0) {
350 byte [] bytes
= response
.GetBuffer ();
351 int len
= UpdateBodyLength ((int) response
.Length
);
352 requestBroker
.Write (requestId
, bytes
, 0, len
);
358 requestBroker
.Flush (requestId
);
359 response
.SetLength (0);
361 } catch (Exception e
) {
362 WebTrace
.WriteLine (e
.ToString ());
367 public override string GetFilePath ()
369 WebTrace
.WriteLine ("GetFilePath()");
373 public override string GetHttpVerbName ()
375 WebTrace
.WriteLine ("GetHttpVerbName()");
379 public override string GetHttpVersion ()
381 WebTrace
.WriteLine ("GetHttpVersion()");
385 public override string GetKnownRequestHeader (int index
)
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
)
400 return headers
[name
] as string;
403 public override string [][] GetUnknownRequestHeaders ()
405 if (unknownHeaders
== 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
]);
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()");
442 public override int GetLocalPort ()
444 WebTrace
.WriteLine ("GetLocalPort()");
448 public override string GetPathInfo ()
450 WebTrace
.WriteLine ("GetPathInfo()");
454 public override byte [] GetPreloadedEntityBody ()
456 WebTrace
.WriteLine ("GetPreloadedEntityBody");
460 public override string GetQueryString ()
462 WebTrace
.WriteLine ("GetQueryString()");
466 public override byte [] GetQueryStringRawBytes ()
468 WebTrace
.WriteLine ("GetQueryStringRawBytes()");
469 if (queryString
== 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)
481 if (queryString
!= null && queryString
.Length
> 0)
482 return result
+ "?" + queryString
;
487 public override string GetRemoteAddress ()
489 WebTrace
.WriteLine ("GetRemoteAddress()");
490 return ((IPEndPoint
) remoteEP
).Address
.ToString ();
493 public override string GetRemoteName ()
495 string ip
= GetRemoteAddress ();
498 IPHostEntry entry
= Dns
.GetHostByName (ip
);
499 name
= entry
.HostName
;
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;
518 case "GATEWAY_INTERFACE":
522 result
= (IsSecure ()) ? "on" : "off";
524 case "SERVER_SOFTWARE":
525 result
= server_software
;
528 result
= base.GetServerVariable (name
);
535 public override string GetUriPath ()
537 WebTrace
.WriteLine ("GetUriPath()");
539 string result
= path
;
540 if (pathInfo
!= null && pathInfo
.Length
> 0)
546 public override bool HeadersSent ()
548 WebTrace
.WriteLine ("HeadersSent() -> " + 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
565 string localPath
= GetFilePathTranslated ();
567 if (!Directory
.Exists (localPath
))
570 string oldPath
= path
;
571 if (!path
.EndsWith ("/"))
575 foreach (string indexFile
in indexFiles
) {
576 string testfile
= Path
.Combine (localPath
, indexFile
);
577 if (File
.Exists (testfile
)) {
590 protected override bool GetRequestData ()
592 return TryDirectory ();
595 int ReadInput (byte [] buffer
, int offset
, int size
)
597 int length
= inputLength
- position
;
602 Buffer
.BlockCopy (inputBuffer
, position
, buffer
, offset
, length
);
610 int localsize
= size
;
611 while (localsize
> 0) {
613 int read
= requestBroker
.Read (requestId
, localsize
, out readBuffer
);
614 Array
.Copy (readBuffer
, 0, buffer
, offset
, read
);
619 return (length
+ size
);
622 public override int ReadEntityBody (byte [] buffer
, int size
)
624 WebTrace
.WriteLine ("ReadEntityBody()");
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)
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) {
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.
665 responseHeaders
.Append (name
);
666 responseHeaders
.Append (": ");
667 responseHeaders
.Append (value);
668 responseHeaders
.Append ("\r\n");