internal virtual void create_env_vars(StringDictionary env_collection, http_message msg) { http_request req = msg.Request; http_response res = msg.Response; DataAccessLayer d = msg.data_layer; String allheader = ""; //Requirements for POST DATA if (req.Header.Verb == "POST" | req.Header.Verb == "PUT") { env_collection.Add("CONTENT_LENGTH", req.Body.Length.ToString()); env_collection.Add("CONTENT_TYPE",req.Header.GetValue("Content-Type")); } // TODO: if you ever implement http authentication or authorization these will need to be adjusted. env_collection.Add("AUTH_TYPE", ""); env_collection.Add("AUTH_USER", ""); env_collection.Add("REMOTE_USER", ""); //Setup for "ALL_HTTP" foreach(String key in req.Header.Values.Keys) { allheader += String.Format("{0}: {1}\r\n", "HTTP_" +key.Replace("-","_").ToUpper(), req.Header.GetValue(key)); env_collection.Add("HTTP_" +key.Replace("-","_").ToUpper(), req.Header.GetValue(key)); } env_collection.Add("ALL_HTTP",allheader); env_collection.Add("ALL_RAW",req.Header.ToString()); env_collection.Add("REQUEST_METHOD", req.Header.Verb.ToUpper()); env_collection.Add("REQUEST_URI", req.Header.Target); env_collection.Add("SCRIPT_NAME", req.script_name); env_collection.Add("REMOTE_ADDR",((IPEndPoint)req.Connection.Client.RemoteEndPoint).Address.ToString()); env_collection.Add("REMOTE_PORT", ((IPEndPoint)req.Connection.Client.RemoteEndPoint).Port.ToString ()); env_collection.Add("QUERY_STRING", req.Uri.Query.Replace("?","")); // TODO: This is a may not alway produce the proper result. PATH_INFO // is intended to be the part of the path that is left over when // the scriptname is part of the path i.e https://test.com/index.php/more/path. // In this example case /more/path would be the PATH_INFO. env_collection.Add("PATH_INFO", req.Uri.AbsolutePath.Replace(req.script_name,"")); if(msg.ClientStream is SslStream) { env_collection.Add("SCHEME", "https"); } else { env_collection.Add("SCHEME", "http"); } env_collection.Add("REDIRECT_STATUS", res.Header.Statuscode); env_collection.Add("GATEWAY_INTERFACE","CGI/1.1"); env_collection.Add("SERVER_PROTOCOL",res.Header.Httpversion); env_collection.Add("COOKIES",req.Header.Cookie); env_collection.Add("SERVER_PORT",req.Uri.Port.ToString()); env_collection.Add("SERVER_NAME",req.Header.Host); env_collection.Add("SERVER_SOFTWARE",res.Header.Server); env_collection.Add("SCRIPT_FILENAME", ((FileDataAccessLayer)d).GetFullPath(req.script_name)); }
internal virtual void worker_DoWork(Object arg) { http_message msg = new http_message(); object[] args = (object[])arg; connection cc = (connection)args[0]; try { webserver_plugin_manager scripters = (webserver_plugin_manager)args[1]; msg.ClientConnection = cc; msg.data_layer = _dataLayer; msg.ClientEndpoint = cc.Client.Client.RemoteEndPoint; msg.Status = "REQ"; msg.Request = new http_request(); msg.Request.Initialize(msg.ClientConnection); worker_ProgressChanged(0, msg); // Redirect these to fix releative path issues if (_dataLayer.DirectoryExists (msg.Request.script_name) && !msg.Request.script_name.EndsWith ("/")) { msg.Response = http_response.Create(msg,true); msg.Response.Header.Statuscode = "301"; msg.Response.Header.Statusmessage = "Moved"; msg.Response.Header.SetValue ("Location", msg.Request.script_name + "/"); msg.Response.Header.ContentLength = 0; msg.Response.Respond(msg.ClientConnection.Client,msg.ClientStream); msg.Status = "FIN"; cc.Processing = false; return; } msg.Status = "DIR"; msg.Response = http_response.Create(msg,true); _directives.process(new object[]{msg.Request,msg.Response,_dataLayer}); msg.Status = "RAM"; if(!scripters.process(new object[] {msg})) { // If you are here then no "scripter" processed the request so // we had better do it. msg.Status = "RES"; msg.Response = http_response.Create(msg, false); msg.Response.Respond(msg.ClientConnection.Client, msg.ClientStream); } msg.Status = "FIN"; cc.processing = false; } catch (Exception ex) { msg.Status = "ERR"; #if DEBUG Console.Error.WriteLine(ex.Message); Console.Error.WriteLine(ex.StackTrace); #endif if(ex is http_exception) { http_exception he = (http_exception)ex; msg.Response = he.response; try{he.send_response(msg.ClientConnection.Client,msg.ClientStream);}catch{} if(he.close_client_connection) { msg.ClientConnection.Client.Client.Disconnect(false); msg.ClientConnection = null; } } else { if(msg.Request != null) { msg.Response = http_response.CreateBasic(msg.Request,500); msg.Response.Header.SetValue( "Server", String.Format("{0}/{1}",Assembly.GetEntryAssembly().GetName().Name,Assembly.GetEntryAssembly().GetName().Version)); msg.Response.Header.SetValue("ContentType","text/html"); msg.Response.Header.SetValue("Cache-Control","no-cache"); msg.Response.Body = new MemoryStream(Encoding.Default.GetBytes(http_function.CreateBasicHTML(ex.ToString()))); msg.Response.Header.SetValue("Content-Length", msg.Response.Body.Length.ToString()); http_function.SendBasicHtml(msg.ClientStream,msg.Response); } else { msg.ClientConnection.Client.Client.Disconnect(false); msg.ClientConnection = null; } } } cc.processing = false; }
public static http_response Create(http_message msg, Boolean basic) { http_request req = msg.Request; http_response res = CreateBasic(req,200); DataAccessLayer d = msg.data_layer; String targetfile = HttpUtility.UrlDecode(req.Uri.AbsolutePath); if (basic) return res; // TODO: Support these as test cases come up... if (req.Header.Verb == "CONNECT" || req.Header.Verb == "OPTIONS" || req.Header.Verb == "DELETE" || req.Header.Verb == "PUT" || req.Header.Verb == "TRACE" || req.Header.Verb == "POST" ) { #if DEBUG Console.Error.WriteLine("Unsupported Method: " + req.Header.Verb); #endif res = CreateBasic(req,501); res.Header.ContentType = "text/plain; charset=" + Encoding.Default.WebName.ToUpper(); byte[] body = Encoding.Default.GetBytes (res.Header.Statusmessage); res.Body.Write (body, 0, body.Length); res.Header.ContentLength = Convert.ToInt32 (res.Body.Length); return res; } if (d.DirectoryExists (targetfile)) { foreach (string index_file in _index_files) { if (d.FileExists (d.PathCombine (targetfile, index_file))) { req.Uri = new HttpUri (HttpUtility.UrlDecode (req.Uri.OriginalString).Replace (targetfile, d.URLPathCombine (req.Uri.AbsolutePath, index_file))); if(msg.ClientStream is SslStream) req.Header.Referer = "https://" + req.Header.Host + targetfile; else req.Header.Referer = "http://" + req.Header.Host + targetfile; targetfile = HttpUtility.UrlDecode (req.Uri.AbsolutePath); req.Header.Target = targetfile; break; } } } if (d.DirectoryExists(targetfile)) { if (_allow_directory_browsing) { res.Header.Statuscode = "200"; res.Header.Statusmessage = "OK"; res.Header.SetValue("Cache-Control","no-cache"); res.Header.SetValue ("Content-Type", "text/html; charset=" + Encoding.Default.WebName.ToUpper()); String body = "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + Encoding.Default.WebName + "\" />\n</head>\n<body>\n"; String[] files = d.GetFiles (targetfile); String scheme = "http://"; if(msg.ClientStream is SslStream) scheme = "https://"; foreach (String file in files) { body += String.Format ("<a href=\"{0}\">{1}</a><br>\n", scheme + req.Header.Host + "/" + HttpUtility.UrlEncode (d.URLPathCombine("",file)).Replace ("%2f", "/"), Path.GetFileName (file)); } body += "\n</body>\n</html>"; byte[] bytes = Encoding.Default.GetBytes (body); res.Header.SetValue ("Content-Length", bytes.Length.ToString ()); res.Body.Write (bytes, 0, (int)bytes.Length); res.End = (int)bytes.Length; } else { throw new http_exception(req,"Direcotry browsing is not permitted.",null,403); } } else if(d.FileExists(targetfile)) { if(!_disable_cache && req.Header.Values.ContainsKey("If-Modified-Since") && DateTime.Parse(req.Header.GetValue("If-Modified-Since")).ToUniversalTime() == d.GetFileDate(targetfile)) { res.Header.Statuscode = "304"; res.Header.Statusmessage = "Not Modified"; res.Header.SetValue("Expires", DateTime.Now.AddDays(1).ToString("r")); res.Header.SetValue("Cache-Control","public must-revalidate max-age=3600"); res.Header.SetValue("Last-Modified",d.GetFileDate(targetfile).ToString("r")); res.Header.ContentLength = 0; } else if (req.Header.Values.ContainsKey ("Range")) { res.Body = d.GetFile(targetfile); res.Header.Statuscode = "206"; res.Header.Statusmessage = "Partial Content"; string range = req.Header.GetValue ("Range"); string[] rangesplit = range.Split (new string[] { "=", "-" }, StringSplitOptions.RemoveEmptyEntries); if (rangesplit.Length > 0 && rangesplit [0].ToLower () != "bytes") throw new http_exception (req,"Range only accepted in bytes!",null,406); if (rangesplit.Length > 1 && rangesplit [1] != null) res.Start = Convert.ToInt32 (rangesplit [1]); if (rangesplit.Length > 2 && rangesplit [2] != null) { res.End = Convert.ToInt32 (rangesplit [2]); } else { res.End = res.Body.Length; } res.Header.ContentLength = Convert.ToInt32 (res.End - res.Start); res.Header.SetValue("Content-Range", String.Format("bytes {0}-{1}/{2}",res.Start, res.End -1,res.End)); } else { res.Body = d.GetFile(targetfile); if(res.Body.Length > 1024 * 1024) { res.Header.SetValue("Accept-Ranges","bytes"); } res.Header.Statuscode = "200"; res.Header.Statusmessage = "OK"; if(!_disable_cache) { res.Header.SetValue("Expires", DateTime.Now.AddDays(1).ToString("r")); res.Header.SetValue("Cache-Control","public must-revalidate max-age=3600"); res.Header.SetValue("Last-Modified",d.GetFileDate(targetfile).ToString("r")); } else res.Header.SetValue("Cache-Control","no-cache"); res.End = res.Body.Length; res.Header.ContentLength = Convert.ToInt32(res.Body.Length); } string ext = Path.GetExtension(req.Uri.AbsolutePath); res.Header.ContentType = mime_types.GetMimeType(ext); } else { throw new http_exception( req, String.Format("{0} was not found on this server",req.Header.Target), null,404); } return res; }