1 // 2 // Mono.WebServer.ModMonoWorker 3 // 4 // Authors: 5 // Gonzalo Paniagua Javier (gonzalo@ximian.com) 6 // Lluis Sanchez Gual (lluis@ximian.com) 7 // 8 // (C) Copyright 2004-2007 Novell, Inc. (http://www.novell.com) 9 // 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: 17 // 18 // The above copyright notice and this permission notice shall be 19 // included in all copies or substantial portions of the Software. 20 // 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. 28 // 29 30 using System; 31 using System.IO; 32 using System.Net.Sockets; 33 using Mono.WebServer.Log; 34 35 namespace Mono.WebServer.Apache 36 { 37 // 38 // ModMonoWorker: The worker that does the initial processing of mod_mono 39 // requests. 40 // 41 class ModMonoWorker: Worker, IDisposable 42 { 43 public NetworkStream Stream; 44 45 readonly ApplicationServer server; 46 ModMonoRequest modRequest; 47 bool closed; 48 int requestId = -1; 49 ModMonoRequestBroker broker; 50 readonly Socket client; 51 ModMonoWorker(Socket client, ApplicationServer server)52 public ModMonoWorker (Socket client, ApplicationServer server) 53 { 54 Stream = new NetworkStream (client, true); 55 this.client = client; 56 this.server = server; 57 } 58 Dispose(Action disposer, string name)59 static void Dispose (Action disposer, string name) 60 { 61 if (disposer == null) 62 return; 63 64 try { 65 disposer (); 66 } catch (Exception ex) { 67 Logger.Write (LogLevel.Error, "While disposing ModMonoWorker. {0} disposing failed with exception:", name); 68 Logger.Write (ex); 69 } 70 } 71 Dispose()72 public void Dispose () 73 { 74 Dispose (() => { 75 if (Stream != null) 76 Stream.Dispose (); 77 }, "Stream"); 78 79 Dispose (() => { 80 if (modRequest != null) 81 modRequest.Dispose (); 82 }, "modRequest"); 83 84 GC.SuppressFinalize (this); 85 } 86 Run(object state)87 public override void Run (object state) 88 { 89 try { 90 InnerRun (); 91 } catch (Exception e) { 92 // FileNotFoundException might be a sign of a bad deployment 93 // once we require the .exe and Mono.WebServer in bin or the GAC. 94 95 // IOException, like EndOfStreamException, might be ok. 96 97 if (!(e is EndOfStreamException)) 98 Logger.Write (e); 99 100 try { 101 // Closing is enough for mod_mono. the module will return a 50x 102 if (Stream != null){ 103 Stream.Close (); 104 Stream = null; 105 } 106 } catch {} 107 if (!server.SingleApplication && broker != null && requestId != -1) { 108 broker.UnregisterRequest (requestId); 109 requestId = -1; 110 } 111 } 112 } 113 GetOrCreateApplication(string vhost, int port, string filepath, string virt)114 VPathToHost GetOrCreateApplication (string vhost, int port, string filepath, string virt) 115 { 116 string final_vdir; 117 string final_pdir; 118 GetPhysicalDirectory (virt, filepath, out final_vdir, out final_pdir); 119 120 //Logger.Write (LogLevel.Error, "final_pdir: {0} final_vdir: {1}", final_pdir, final_vdir); 121 VPathToHost vapp = server.GetApplicationForPath (vhost, port, virt, false); 122 if (vapp == null) { 123 // Don't know why this breaks mod-mono-server2.exe, but not mod-mono-server.exe 124 //final_pdir = "file://" + final_pdir; 125 if (final_vdir [0] != '/') 126 final_vdir = "/" + final_vdir; 127 server.AddApplication (vhost, port, final_vdir, final_pdir); 128 vapp = server.GetApplicationForPath (vhost, port, virt, false); 129 } 130 131 return vapp; 132 } 133 GetPhysicalDirectory(string vfile, string pfile, out string final_vdir, out string final_pdir)134 internal static void GetPhysicalDirectory (string vfile, string pfile, out string final_vdir, out string final_pdir) 135 { 136 string vdir = Path.GetDirectoryName (vfile); 137 string pdir = Path.GetDirectoryName (pfile); 138 var vinfo = new DirectoryInfo (vdir); 139 var pinfo = new DirectoryInfo (pdir); 140 final_pdir = null; 141 final_vdir = null; 142 while (vinfo != null && pinfo != null) { 143 if (IsRootDirectory (pinfo)) { 144 final_pdir = pinfo.ToString (); 145 final_vdir = vinfo.ToString (); 146 break; 147 } 148 if (pinfo.Name != vinfo.Name) { 149 final_vdir = vinfo.ToString (); 150 break; 151 } 152 pinfo = pinfo.Parent; 153 vinfo = vinfo.Parent; 154 } 155 if (final_pdir == null) { 156 final_pdir = pinfo.ToString (); 157 } 158 if (final_vdir == null) { 159 final_vdir = vinfo.ToString (); 160 } 161 } 162 IsRootDirectory(DirectoryInfo info)163 static bool IsRootDirectory (DirectoryInfo info) 164 { 165 if (!info.Exists) 166 return false; 167 168 return File.Exists (Path.Combine (info.FullName, "Global.asax")) 169 || File.Exists (Path.Combine (info.FullName, "global.asax")) 170 || Directory.Exists (Path.Combine (info.FullName, "bin")); 171 } 172 InnerRun()173 void InnerRun () 174 { 175 requestId = -1; 176 broker = null; 177 178 var rr = new RequestReader (client); 179 if (rr.ShuttingDown) { 180 Close (); 181 server.Stop (); 182 return; 183 } 184 185 string vhost = rr.Request.GetRequestHeader ("Host"); 186 int port = -1; 187 if (vhost != null) { 188 int lead = vhost.IndexOf('['); 189 int follow = lead >= 0 ? vhost.IndexOf(']') : -1; 190 if (follow > lead) { 191 //ipv6 address 192 int colon = vhost.IndexOf("]:", StringComparison.Ordinal); 193 if (colon != -1) { 194 Int32.TryParse (vhost.Substring (colon + 2), out port); 195 vhost = vhost.Substring(0, colon + 1); 196 } 197 } else { 198 //ipv4 or hostname 199 int colon = vhost.IndexOf (':'); 200 if (colon != -1) { 201 Int32.TryParse (vhost.Substring (colon + 1), out port); 202 vhost = vhost.Substring (0, colon); 203 } 204 } 205 if (port <= 0 || port > 65535) { 206 //No port specified, Int32.TryParse failed or invalid port number 207 port = 80; 208 } 209 } 210 211 string vServerName = rr.Request.GetVirtualServerName () ?? vhost; 212 213 VPathToHost vapp; 214 string vpath = rr.GetUriPath (); 215 string path = rr.GetPhysicalPath (); 216 if (path == null) { 217 vapp = server.GetApplicationForPath (vServerName, port, vpath, false); 218 } else { 219 vapp = GetOrCreateApplication (vServerName, port, path, vpath); 220 } 221 222 if (vapp == null) { 223 rr.NotFound (); 224 Stream.Close (); 225 Stream = null; 226 return; 227 } 228 229 var host = (ModMonoApplicationHost) vapp.AppHost; 230 if (host == null) { 231 rr.NotFound (); 232 Stream.Close (); 233 Stream = null; 234 return; 235 } 236 modRequest = rr.Request; 237 238 if (!server.SingleApplication) { 239 broker = (ModMonoRequestBroker) vapp.RequestBroker; 240 broker.UnregisterRequestEvent += OnUnregisterRequest; 241 requestId = broker.RegisterRequest (this); 242 } 243 244 host.ProcessRequest (requestId, 245 modRequest.GetHttpVerbName(), 246 modRequest.GetQueryString(), 247 modRequest.GetUri(), 248 modRequest.GetProtocol(), 249 modRequest.GetLocalAddress(), 250 modRequest.GetServerPort(), 251 modRequest.GetRemoteAddress(), 252 modRequest.GetRemotePort(), 253 modRequest.GetRemoteName(), 254 modRequest.GetAllHeaders(), 255 modRequest.GetAllHeaderValues(), 256 (requestId == -1) ? this : null); 257 } 258 OnUnregisterRequest(object sender, UnregisterRequestEventArgs args)259 void OnUnregisterRequest (object sender, UnregisterRequestEventArgs args) 260 { 261 var broker = sender as BaseRequestBroker; 262 if (broker != null) 263 broker.UnregisterRequestEvent -= OnUnregisterRequest; 264 265 if (requestId == -1 || requestId != args.RequestId) 266 return; 267 268 requestId = -1; 269 } 270 Read(byte[] buffer, int position, int size)271 public override int Read (byte[] buffer, int position, int size) 272 { 273 return modRequest.GetClientBlock (buffer, position, size); 274 } 275 Write(byte[] buffer, int position, int size)276 public override void Write (byte[] buffer, int position, int size) 277 { 278 modRequest.SendResponseFromMemory (buffer, position, size); 279 } 280 Write(IntPtr ptr, int size)281 public void Write (IntPtr ptr, int size) 282 { 283 modRequest.SendResponseFromMemory (ptr, size); 284 } 285 Close()286 public override void Close () 287 { 288 if (closed) 289 return; 290 291 closed = true; 292 try { 293 modRequest.Close (); 294 } catch {} // ignore any error 295 try { 296 if (Stream != null) { 297 Stream.Close (); 298 Stream = null; 299 } 300 } catch {} 301 } 302 Flush()303 public override void Flush () 304 { 305 //modRequest.Flush (); No-op 306 } 307 IsConnected()308 public override bool IsConnected () 309 { 310 return modRequest.IsConnected (); 311 } 312 SendFile(string filename)313 public void SendFile (string filename) 314 { 315 modRequest.SendFile (filename); 316 } 317 GetServerVariable(string name)318 public string GetServerVariable (string name) 319 { 320 return modRequest.GetServerVariable (name); 321 } 322 SetStatusCodeLine(int code, string status)323 public void SetStatusCodeLine (int code, string status) 324 { 325 modRequest.SetStatusCodeLine (code, status); 326 } 327 SetResponseHeader(string name, string value)328 public void SetResponseHeader (string name, string value) 329 { 330 modRequest.SetResponseHeader (name, value); 331 } 332 SetOutputBuffering(bool doBuffer)333 public void SetOutputBuffering (bool doBuffer) 334 { 335 modRequest.SetOutputBuffering (doBuffer); 336 } 337 338 public bool HeadersSent { 339 get { return modRequest.HeadersSent; } 340 } 341 } 342 } 343