//========================================================================== // File: HttpServer.cs // // Summary: Implements an HttpServer to be used by the HttpServerChannel class // // // Classes: internal sealed HttpServer // internal sealed ReqMessageParser // private RequestArguments // // By : // Ahmad Tantawy popsito82@hotmail.com // Ahmad Kadry kadrianoz@hotmail.com // Hussein Mehanna hussein_mehanna@hotmail.com // //========================================================================== using System; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using System.Collections; using System.Runtime.Remoting.Channels; using System.IO; using System.Net; using System.Runtime.Remoting.Messaging; namespace System.Runtime.Remoting.Channels.Http { internal class RequestArguments { public RequestArguments (Socket socket, HttpServerTransportSink sink) { NetworkStream ns = new NetworkStream (socket); InputStream = ns; OutputStream = ns; Sink = sink; } public RequestArguments (Stream inputStream, Stream outputStream, HttpServerTransportSink sink) { InputStream = inputStream; OutputStream = outputStream; Sink = sink; } public Stream InputStream; public Stream OutputStream; public HttpServerTransportSink Sink; } internal sealed class HttpServer { private TcpListener listener=null; private const int nTimeOut = 1000; public HttpServer(int port) { try { listener = new TcpListener(port); } catch(NullReferenceException) { Console.WriteLine("The port is bound to another application"); } } public static void ProcessRequest (object reqInfo) { if(reqInfo as RequestArguments == null) return; RequestArguments reqArg = (RequestArguments)reqInfo; //Step (1) Start Reciceve the header ArrayList Headers = RecieveHeader (reqArg); //Step (2) Start Parse the header IDictionary HeaderFields = new Hashtable(); IDictionary CustomHeaders = new Hashtable(); if (!ParseHeader (reqArg, Headers, HeaderFields, CustomHeaders)) return; //Step (3) if (!CheckRequest (reqArg, HeaderFields, CustomHeaders)) return; //Step (4) Recieve the entity body byte[] buffer; object len = HeaderFields["content-length"]; if (len != null) { buffer = new byte [(int)len]; if (!RecieveEntityBody (reqArg, buffer)) return; } else buffer = new byte [0]; //Step (5) SendRequestForChannel (reqArg, HeaderFields, CustomHeaders, buffer); } private static ArrayList RecieveHeader (RequestArguments reqArg) { bool bLastLine = false; bool bEndOfLine = false; byte[] buffer = new byte[1024]; ArrayList Headers = new ArrayList(); Stream ist = reqArg.InputStream; int index =0; while (!bLastLine) { //recieve line by line index = 0; bEndOfLine = false; //Step (1) is it an empty line? ist.Read (buffer, index, 1); if(buffer[index++]==13) { ist.Read (buffer, index, 1); bLastLine=true; bEndOfLine = true; } //Step (2) recieve line bytes while (!bEndOfLine) { ist.Read (buffer, index, 1); if(buffer [index++]==13) { bEndOfLine = true; ist.Read (buffer,index,1); } } //Step (3) convert bytes to a string if (bLastLine) continue; Headers.Add (Encoding.ASCII.GetString (buffer,0,index)); }//end while loop return Headers; } private static bool ParseHeader (RequestArguments reqArg, ArrayList Headers, IDictionary HeaderFields, IDictionary CustomHeaders) { for (int i=0;i break; } return false; }//end catch return true; } private static bool SendRequestForChannel (RequestArguments reqArg, IDictionary HeaderFields, IDictionary CustomHeaders, byte[] buffer) { TransportHeaders THeaders = new TransportHeaders(); Stream stream = new MemoryStream(buffer); if(stream.Position !=0) stream.Seek(0,SeekOrigin.Begin); THeaders[CommonTransportKeys.RequestUri] = FixURI((string)HeaderFields["request-url"]); THeaders[CommonTransportKeys.ContentType]= HeaderFields["content-type"]; THeaders[CommonTransportKeys.RequestVerb]= HeaderFields["method"]; THeaders[CommonTransportKeys.HttpVersion] = HeaderFields["http-version"]; THeaders[CommonTransportKeys.UserAgent] = HeaderFields["user-agent"]; THeaders[CommonTransportKeys.Host] = HeaderFields["host"]; THeaders[CommonTransportKeys.SoapAction] = HeaderFields["SOAPAction"]; foreach(DictionaryEntry DictEntry in CustomHeaders) { THeaders[DictEntry.Key.ToString()] = DictEntry.Value.ToString(); } reqArg.Sink.ServiceRequest (reqArg, stream, THeaders); return true; } private static string FixURI(string RequestURI) { if(RequestURI.IndexOf ( '.' ) == -1) return RequestURI; else return RequestURI.Substring(1); } public static bool SendResponse (RequestArguments reqArg, int httpStatusCode, ITransportHeaders headers, Stream responseStream) { byte [] headersBuffer = null; byte [] entityBuffer = null; StringBuilder responseStr; String reason = null; if (headers != null && headers[CommonTransportKeys.HttpStatusCode] != null) { // The formatter can override the result code httpStatusCode = int.Parse ((string)headers [CommonTransportKeys.HttpStatusCode]); reason = (string) headers [CommonTransportKeys.HttpReasonPhrase]; } if (reason == null) reason = GetReasonPhrase (httpStatusCode); //Response Line responseStr = new StringBuilder ("HTTP/1.0 " + httpStatusCode + " " + reason + "\r\n" ); if (headers != null) { foreach (DictionaryEntry entry in headers) { string key = entry.Key.ToString(); if (key != CommonTransportKeys.HttpStatusCode && key != CommonTransportKeys.HttpReasonPhrase) responseStr.Append(key + ": " + entry.Value.ToString() + "\r\n"); } } responseStr.Append("Server: Mono Remoting, Mono CLR " + System.Environment.Version.ToString() + "\r\n"); if(responseStream != null && responseStream.Length!=0) { responseStr.Append("Content-Length: "+responseStream.Length.ToString()+"\r\n"); entityBuffer = new byte[responseStream.Length]; responseStream.Seek(0 , SeekOrigin.Begin); responseStream.Read(entityBuffer,0,entityBuffer.Length); } else responseStr.Append("Content-Length: 0\r\n"); responseStr.Append("X-Powered-By: Mono\r\n"); responseStr.Append("Connection: close\r\n"); responseStr.Append("\r\n"); headersBuffer = Encoding.ASCII.GetBytes (responseStr.ToString()); try { //send headersBuffer reqArg.OutputStream.Write (headersBuffer, 0, headersBuffer.Length); if (entityBuffer != null) reqArg.OutputStream.Write (entityBuffer, 0, entityBuffer.Length); } catch { // //may be its the client's fault so just return with false return false; } return true; } internal static string GetReasonPhrase (int HttpStatusCode) { switch (HttpStatusCode) { case 100 : return "Continue" ; case 101 :return "Switching Protocols"; case 200 :return "OK"; case 201 :return "Created"; case 202 :return "Accepted"; case 203 :return "Non-Authoritative Information"; case 204 :return "No Content"; case 205 :return "Reset Content"; case 206 :return "Partial Content"; case 300 :return "Multiple Choices"; case 301 :return "Moved Permanently"; case 302 :return "Found"; case 303 :return "See Other"; case 304 :return "Not Modified"; case 305 :return "Use Proxy"; case 307 :return "Temporary Redirect"; case 400 :return "Bad Request"; case 401 :return "Unauthorized"; case 402 :return "Payment Required"; case 403 :return "Forbidden"; case 404 :return "Not Found"; case 405 :return "Method Not Allowed"; case 406 :return "Not Acceptable"; case 407 :return "Proxy Authentication Required"; case 408 :return "Request Time-out"; case 409 :return "Conflict"; case 410 :return "Gone"; case 411 :return "Length Required"; case 412 :return "Precondition Failed"; case 413 :return "Request Entity Too Large"; case 414 :return "Request-URI Too Large"; case 415 :return "Unsupported Media Type"; case 416 :return "Requested range not satisfiable"; case 417 :return "Expectation Failed"; case 500 :return "Internal Server Error"; case 501 :return "Not Implemented"; case 502 :return "Bad Gateway"; case 503 :return "Service Unavailable"; case 504 :return "Gateway Time-out"; case 505 :return "HTTP Version not supported"; default: return ""; } } } internal sealed class ReqMessageParser { private const int nCountReq = 14; private const int nCountEntity = 15; private static bool bInitialized = false; private static String [] ReqRegExpString = new String [nCountReq ]; private static String [] EntityRegExpString = new String[nCountEntity]; private static Regex [] ReqRegExp = new Regex[nCountReq]; private static Regex [] EntityRegExp = new Regex[nCountEntity]; public ReqMessageParser () { } public static bool ParseHeaderField(string buffer,IDictionary headers) { try { if(!bInitialized) { Initialize(); bInitialized =true; } if(IsRequestField(buffer,headers)) return true; if(IsEntityField(buffer,headers)) return true ; } catch(Exception ) { // } //Exception return false; } private static bool Initialize() { if(bInitialized) return true; bInitialized = true; //initialize array //Create all the Regular expressions InitializeRequestRegExp(); InitiazeEntityRegExp(); for(int i=0;i\\S+)(\\s*:\\s*)(?\\S+(\\s|\\S)*\\S)(\\s*)",RegexOptions.Compiled); Match m = CustomHeaderEx.Match(buffer); if(!m.Success) return false; CustomHeader.Add(m.Groups["header"].Value,m.Groups["field"].Value); return true; } //******************************************************** //REQUEST private static bool Request_accept(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[0].Match(buffer); if(!m.Success) return false; HeaderItems.Add("accept",m.Groups["accept"].Value); return true; } private static bool Request_accept_charset(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[1].Match(buffer); if(!m.Success) return false; HeaderItems.Add("accept-charset",m.Groups["accept_charset"].Value); return true; } private static bool Request_accept_encoding(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[2].Match(buffer); if(!m.Success) return false; HeaderItems.Add("accept-encoding",m.Groups["accept_encoding"].Value); return true; } private static bool Request_authorization(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[3].Match(buffer); if(!m.Success) return false; HeaderItems.Add("authorization",m.Groups["authorization"].Value); return true; } private static bool Request_accept_language(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[4].Match(buffer); if(!m.Success) return false; HeaderItems.Add("accept-language",m.Groups["accept_language"].Value); return true; } private static bool Request_from(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[5].Match(buffer); if(!m.Success) return false; HeaderItems.Add("from",m.Groups["from"].Value); return true; } private static bool Request_host(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[6].Match(buffer); if(!m.Success) return false; HeaderItems.Add("host",m.Groups["host"].Value); return true; } private static bool Request_modified(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[7].Match(buffer); if(!m.Success) return false; HeaderItems.Add("modified",m.Groups["modified"].Value); return true; } private static bool Request_proxy_authorization(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[8].Match(buffer); if(!m.Success) return false; HeaderItems.Add("proxy-authorization",m.Groups["proxy_authorization"].Value); return true; } private static bool Request_range(string buffer , IDictionary HeaderItems) { Match m = ReqRegExp[9].Match(buffer); if(!m.Success) return false; HeaderItems.Add("range",m.Groups["range"].Value); return true; } private static bool Request_user_agent(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[10].Match(buffer); if(!m.Success) return false; HeaderItems.Add("user-agent",m.Groups["user_agent"].Value); return true; } private static bool Request_expect(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[11].Match(buffer); if(!m.Success) return false; HeaderItems.Add("expect",m.Groups["expect"].Value); return true; } private static bool Request_connection(string buffer,IDictionary HeaderItems) { Match m = ReqRegExp[12].Match(buffer); if(!m.Success) return false; HeaderItems.Add("connection",m.Groups["connection"].Value); return true; } private static bool Request_request_line(string buffer, IDictionary HeaderItems) { Match m = ReqRegExp[13].Match(buffer); if(!m.Success) return false; //ReqRegExpString[13] = "(?\\w+)(\\s+)(?\\S+)(\\s+)(?\\S+)"; HeaderItems.Add("method",m.Groups["method"].Value); HeaderItems.Add("request-url",m.Groups["request_url"].Value); HeaderItems.Add("http-version",m.Groups["http_version"].Value); return true; } //******************************************************** //******************************************************** //ENTITY private static bool Entity_allow(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[0].Match(buffer); if(!m.Success) return false; HeaderItems.Add("allow",m.Groups["allow"].Value); return true; } private static bool Entity_content_encoding(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[1].Match(buffer); if(!m.Success) return false; HeaderItems.Add("content-encoding",m.Groups["content_encoding"].Value); return true; } private static bool Entity_content_language(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[2].Match(buffer); if(!m.Success) return false; HeaderItems.Add("content-language",m.Groups["content_language"].Value); return true; } private static bool Entity_content_length(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[3].Match(buffer); if(!m.Success) return false; int length; try { length = Int32.Parse(m.Groups["content_length"].ToString()); } catch (Exception ) { // return false; } HeaderItems.Add("content-length",length); return true; } private static bool Entity_content_range(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[4].Match(buffer); if(!m.Success) return false; HeaderItems.Add("content-range",m.Groups["content_range"].Value); return true; } private static bool Entity_content_type(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[5].Match(buffer); if(!m.Success) return false; HeaderItems.Add("content-type",m.Groups["content_type"].Value); return true; } private static bool Entity_content_version(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[6].Match(buffer); if(!m.Success) return false; HeaderItems.Add("content-version",m.Groups["content_version"].Value); return true; } private static bool Entity_dervied_from(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[7].Match(buffer); if(!m.Success) return false; HeaderItems.Add("dervied-from",m.Groups["dervied_from"].Value); return true; } private static bool Entity_expires(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[8].Match(buffer); if(!m.Success) return false; HeaderItems.Add("expires",m.Groups["expires"].Value); return true; } private static bool Entity_last_modified(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[9].Match(buffer); if(!m.Success) return false; HeaderItems.Add("last-modified",m.Groups["last_modified"].Value); return true; } private static bool Entity_link(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[10].Match(buffer); if(!m.Success) return false; HeaderItems.Add("link",m.Groups["link"].Value); return true; } private static bool Entity_title(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[11].Match(buffer); if(!m.Success) return false; HeaderItems.Add("title",m.Groups["title"].Value); return true; } private static bool Entity_transfere_encoding(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[12].Match(buffer); if(!m.Success) return false; HeaderItems.Add("transfere-encoding",m.Groups["transfere_encoding"].Value); return true; } private static bool Entity_url_header(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[13].Match(buffer); if(!m.Success) return false; HeaderItems.Add("url-header",m.Groups["url_header"].Value); return true; } private static bool Entity_extension_header(string buffer,IDictionary HeaderItems) { Match m = EntityRegExp[14].Match(buffer); if(!m.Success) return false; HeaderItems.Add("extension-header",m.Groups["extension_header"].Value); return true; } //******************************************************** } }