5 // Daniel Lopez Ridruejo
6 // Gonzalo Paniagua Javier
8 // Copyright (c) 2002 Daniel Lopez Ridruejo.
9 // (c) 2002,2003 Ximian, Inc.
10 // All rights reserved.
11 // (C) Copyright 2004-2009 Novell, Inc. (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Collections
;
35 using System
.Diagnostics
;
37 using System
.Net
.Sockets
;
38 using System
.Runtime
.InteropServices
;
39 using System
.Runtime
.CompilerServices
;
41 using System
.Threading
;
42 using System
.Collections
.Generic
;
44 namespace Mono
.WebServer
72 get { return changed; }
73 set { changed = value; }
76 public bool OutputBuffering
{
77 get { return outputBuffering; }
79 changed
|= (value != outputBuffering
);
80 outputBuffering
= value;
85 public class ModMonoRequest
87 const int MAX_STRING_SIZE
= 1024 * 10;
88 const int INITIAL_MEMORY_STREAM_SIZE
= 2048;
92 MemoryStream reader_ms
;
93 MemoryStream writer_ms
;
95 Dictionary
<string, string> serverVariables
;
102 string remoteAddress
;
107 bool setupClientBlockCalled
;
108 Dictionary
<string, string> headers
;
111 StringBuilder out_headers
= new StringBuilder ();
113 string physical_path
;
114 ModMonoConfig mod_mono_config
;
116 static bool use_libc
;
118 static ModMonoRequest ()
120 if (Environment
.GetEnvironmentVariable ("XSP_NO_LIBC") != null)
125 using (StreamReader sr
= new StreamReader (File
.OpenRead ("/proc/sys/kernel/ostype")))
126 os
= sr
.ReadToEnd ();
127 use_libc
= os
.StartsWith ("Linux");
132 public ModMonoRequest (Socket client
)
134 mod_mono_config
.OutputBuffering
= true;
135 this.client
= client
;
136 reader_ms
= new MemoryStream (INITIAL_MEMORY_STREAM_SIZE
);
137 writer_ms
= new MemoryStream (INITIAL_MEMORY_STREAM_SIZE
);
138 reader
= new BinaryReader (reader_ms
);
139 writer
= new BinaryWriter (writer_ms
);
140 serverVariables
= new Dictionary
<string, string> (StringComparer
.OrdinalIgnoreCase
);
144 public bool HeadersSent
{
145 get { return headers_sent; }
148 public bool ShuttingDown
{
149 get { return shutdown; }
152 void FillBuffer (int count
)
154 // This will "reset" the stream
155 int capacity
= reader_ms
.Capacity
;
156 if (capacity
> count
)
157 reader_ms
.Capacity
= capacity
;
159 reader_ms
.Capacity
= count
;
161 reader_ms
.Seek (0, SeekOrigin
.Begin
);
162 byte[] buffer
= reader_ms
.GetBuffer ();
163 int received
= client
.Receive (buffer
, count
, SocketFlags
.None
);
164 reader_ms
.SetLength (received
);
169 client
.Send (writer_ms
.GetBuffer (), (int) writer_ms
.Length
, SocketFlags
.None
);
170 writer_ms
.Position
= 0;
171 writer_ms
.SetLength (0);
174 // KEEP IN SYNC WITH mod_mono.h!!
175 const byte protocol_version
= 9;
176 void GetInitialData ()
180 byte cmd
= reader
.ReadByte ();
181 shutdown
= (cmd
== 0);
185 if (cmd
!= protocol_version
) {
186 string msg
= String
.Format ("mod_mono and xsp have different versions. Expected '{0}', got {1}", protocol_version
, cmd
);
187 Console
.WriteLine (msg
);
188 Console
.Error
.WriteLine (msg
);
189 throw new InvalidOperationException (msg
);
192 Int32 dataSize
= reader
.ReadInt32 ();
193 FillBuffer (dataSize
);
195 verb
= ReadString ();
196 vServerName
= ReadString ();
198 queryString
= ReadString ();
199 protocol
= ReadString ();
200 localAddress
= ReadString ();
201 serverPort
= reader
.ReadInt32 ();
202 remoteAddress
= ReadString ();
203 remotePort
= reader
.ReadInt32 ();
204 remoteName
= ReadString ();
205 reader
.ReadInt32 (); // This is autoApp!!! (unused!?
206 int nheaders
= reader
.ReadInt32 ();
207 headers
= new Dictionary
<string, string> (StringComparer
.OrdinalIgnoreCase
);
209 for (int i
= 0; i
< nheaders
; i
++) {
210 string key
= ReadString ();
211 if (headers
.ContainsKey (key
)) {
212 Console
.WriteLine ("WARNING: duplicate header found! Overwriting old value with the new one.");
213 headers
[key
] = ReadString ();
215 headers
.Add (key
, ReadString ());
218 if (reader
.ReadByte () != 0)
219 physical_path
= ReadString ();
224 int size
= reader
.ReadInt32 ();
225 if (size
< 0 || size
> MAX_STRING_SIZE
)
226 throw new ArgumentOutOfRangeException ("size", "Abnormal string size " + size
);
228 byte [] buf
= new byte [size
];
234 chunk
= reader
.Read (buf
, total
, size
- total
);
236 throw new IOException ("Lost connection with mod_mono");
239 } while ((chunk
> 0 && total
< size
));
241 s
= Encoding
.UTF8
.GetString (buf
);
248 void WriteString (string s
)
250 byte [] bytes
= Encoding
.UTF8
.GetBytes (s
);
251 writer
.Write (bytes
.Length
);
252 writer
.Write (bytes
);
255 public void Decline ()
257 writer
.Write ((int) Cmd
.DECLINE_REQUEST
);
261 public void NotFound ()
263 writer
.Write ((int) Cmd
.NOT_FOUND
);
267 public string GetProtocol ()
272 public string GetHttpVerbName ()
277 public void SendResponseFromMemory (IntPtr ptr
, int length
)
281 writer
.Write ((int) Cmd
.SEND_FROM_MEMORY
);
282 writer
.Write (length
);
290 if (writer_ms
.Position
!= 0)
291 throw new Exception ("Should not happen... We called Send()!");
293 int size
= System
.Math
.Min (16384, length
);
294 if (writer_ms
.Capacity
< size
)
295 writer_ms
.Capacity
= size
;
297 byte [] buf
= writer_ms
.GetBuffer ();
298 Marshal
.Copy (ptr
, buf
, 0, size
);
300 client
.Send (buf
, size
, SocketFlags
.None
);
304 unsafe int Send (IntPtr ptr
, int len
)
307 byte *bptr
= (byte *) ptr
.ToPointer ();
308 while (total
< len
) {
310 int n
= send_libc (client
.Handle
.ToInt32 (), bptr
+ total
, (IntPtr
) (len
- total
), (int) 0x4000);
313 } else if (Marshal
.GetLastWin32Error () != 4 /* EINTR */) {
314 throw new IOException ();
321 [DllImport ("libc", SetLastError
=true, EntryPoint
="send")]
322 unsafe extern static int send_libc (int s
, byte *buffer
, IntPtr len
, int flags
);
324 public void SendResponseFromMemory (byte [] data
, int position
, int length
)
328 writer
.Write ((int) Cmd
.SEND_FROM_MEMORY
);
329 writer
.Write (length
);
331 client
.Send (data
, position
, length
, SocketFlags
.None
);
334 public void SendFile (string filename
)
338 writer
.Write ((int) Cmd
.SEND_FILE
);
339 WriteString (filename
);
345 if (!mod_mono_config
.Changed
)
348 mod_mono_config
.Changed
= false;
349 writer
.Write ((int) Cmd
.SET_CONFIGURATION
);
350 writer
.Write (Convert
.ToByte (mod_mono_config
.OutputBuffering
));
353 void BufferHeaders ()
358 writer
.Write ((int) Cmd
.SET_RESPONSE_HEADERS
);
359 WriteString (out_headers
.ToString ());
364 public void SetResponseHeader (string name
, string value)
367 out_headers
.AppendFormat ("{0}\0{1}\0", name
, value);
370 public void SetOutputBuffering (bool doBuffer
)
372 mod_mono_config
.OutputBuffering
= doBuffer
;
375 public string [] GetAllHeaders ()
377 ICollection k
= headers
.Keys
;
378 string [] keys
= new string [k
.Count
];
383 public string [] GetAllHeaderValues ()
385 ICollection v
= headers
.Values
;
386 string [] values
= new string [v
.Count
];
387 v
.CopyTo (values
, 0);
391 public string GetRequestHeader (string name
)
393 if (headers
.ContainsKey (name
))
394 return headers
[name
] as string;
398 void GetServerVariables ()
400 writer
.Write ((int) Cmd
.GET_SERVER_VARIABLES
);
404 int blockSize
= reader
.ReadInt32 ();
406 FillBuffer (blockSize
);
407 int nvars
= reader
.ReadInt32 ();
409 string key
= ReadString ();
410 if (serverVariables
.ContainsKey (key
)) {
411 Console
.WriteLine ("WARNING! Duplicate server variable found. Overwriting old value with the new one.");
412 serverVariables
[key
] = ReadString ();
414 serverVariables
.Add (key
, ReadString ());
419 got_server_vars
= true;
422 public string GetServerVariable (string name
)
424 if (!got_server_vars
)
425 GetServerVariables ();
427 if (serverVariables
.ContainsKey (name
))
428 return (string) serverVariables
[name
];
433 public string GetPhysicalPath ()
435 return physical_path
;
438 public string GetUri ()
443 public string GetVirtualServerName ()
448 public string GetQueryString ()
453 // May be different from Connection.GetLocalPort depending on Apache configuration,
454 // for things like self referential URLs, etc.
455 public int GetServerPort ()
460 public string GetRemoteAddress ()
462 return remoteAddress
;
465 public string GetRemoteName ()
470 public string GetLocalAddress ()
475 public int GetLocalPort ()
480 writer
.Write ((int) Cmd
.GET_LOCAL_PORT
);
483 localPort
= reader
.ReadInt32 ();
487 public int GetRemotePort ()
495 writer
.Write ((int) Cmd
.CLOSE
);
499 public int SetupClientBlock ()
501 if (setupClientBlockCalled
)
505 setupClientBlockCalled
= true;
506 writer
.Write ((int) Cmd
.SETUP_CLIENT_BLOCK
);
509 int i
= reader
.ReadInt32 ();
514 public bool IsConnected ()
516 writer
.Write ((int) Cmd
.IS_CONNECTED
);
519 int i
= reader
.ReadInt32 ();
523 public bool ShouldClientBlock ()
525 writer
.Write ((int) Cmd
.SHOULD_CLIENT_BLOCK
);
528 int i
= reader
.ReadInt32 ();
532 public int GetClientBlock ([Out
] byte [] bytes
, int position
, int size
)
534 if (SetupClientBlock () != 0) return 0;
537 * turns out that that GET_CLIENT_BLOCK (ap_get_client_block) can
538 * return -1 if a socket is closed
540 writer
.Write ((int) Cmd
.GET_CLIENT_BLOCK
);
545 int i
= reader
.ReadInt32 ();
550 throw new Exception ("Houston...");
553 return reader
.Read (bytes
, position
, i
);
556 public void SetStatusCodeLine (int code
, string status
)
558 writer
.Write ((int) Cmd
.SET_STATUS
);
560 WriteString (status
);