| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- //==========================================================================
- // 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 [email protected]
- // Ahmad Kadry [email protected]
- // Hussein Mehanna [email protected]
- //
- //==========================================================================
- //
- // Permission is hereby granted, free of charge, to any person obtaining
- // a copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to
- // permit persons to whom the Software is furnished to do so, subject to
- // the following conditions:
- //
- // The above copyright notice and this permission notice shall be
- // included in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- //
- 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
- {
- static int count;
- Socket socket;
-
- public RequestArguments (Socket socket, HttpServerTransportSink sink)
- {
- Id = count++;
- NetworkStream ns = new NetworkStream (socket);
- this.Stream = ns;
- this.socket = socket;
- Sink = sink;
- }
-
- public void Process ()
- {
- HttpServer.ProcessRequest (this);
- }
-
- public IPAddress ClientAddress
- {
- get {
- IPEndPoint ep = socket.RemoteEndPoint as IPEndPoint;
- if (ep != null) return ep.Address;
- else return null;
- }
- }
-
- public void Close()
- {
- Stream.Close();
- socket.Close();
- }
-
- public int Id;
- public Stream Stream;
- public HttpServerTransportSink Sink;
- }
- internal sealed class HttpServer
- {
- static HttpServer ()
- {
- Array.Sort (knownHeaders);
- }
-
- public static void ProcessRequest (RequestArguments reqArg)
- {
- try {
- //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);
- } finally {
- reqArg.Close ();
- }
- }
- private static ArrayList RecieveHeader (RequestArguments reqArg)
- {
- bool bLastLine = false;
- bool bEndOfLine = false;
-
- byte[] buffer = new byte[1024];
- ArrayList Headers = new ArrayList();
-
- Stream ist = reqArg.Stream;
- 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)
- {
- // The first "header" is the method
- string[] met = ((string) Headers [0]).Split (' ');
- HeaderFields.Add ("method", met[0]);
- if (met.Length >= 1)
- HeaderFields.Add ("request-url", met [1]);
-
- if (met.Length >= 2)
- HeaderFields.Add ("http-version", met [2]);
-
- for (int i = 1; i < Headers.Count; i++)
- {
- string header = (string) Headers [i];
- int p = header.IndexOf (':');
-
- string id;
- object val;
- if (p == -1) {
- id = header.Trim ().ToLower ();
- val = "";
- } else {
- id = header.Substring (0, p).Trim ().ToLower ();
- val = header.Substring (p + 1).Trim ();
- }
-
- if (Array.BinarySearch (knownHeaders, id) >= 0) {
- if (id == "content-length")
- val = int.Parse ((string)val);
- HeaderFields [id] = val;
- }
- else {
- CustomHeaders [id] = val;
- }
- }
- return true;
- }
- private static bool CheckRequest (RequestArguments reqArg, IDictionary HeaderFields, IDictionary CustomHeaders)
- {
- string temp;
-
- if (HeaderFields["expect"] as string == "100-continue")
- SendResponse (reqArg, 100, null, null);
- //Check the method
- temp = HeaderFields["method"].ToString();
- if (temp != "POST")
- return true;
- //Check for the content-length field
- if (HeaderFields["content-length"]==null)
- {
- SendResponse (reqArg, 411, null, null);
- return false;
- }
- return true;
- }
-
- private static bool RecieveEntityBody (RequestArguments reqArg, byte[] buffer)
- {
- try
- {
- int nr = 0;
- while (nr < buffer.Length)
- nr += reqArg.Stream.Read (buffer, nr, buffer.Length - nr);
- }
- catch (SocketException e)
- {
- switch(e.ErrorCode)
- {
- case 10060 : //TimeOut
- SendResponse (reqArg, 408, null, null);
- break;
- default :
- throw e;
- }
-
- 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"];
- THeaders[CommonTransportKeys.IPAddress] = reqArg.ClientAddress;
- THeaders[CommonTransportKeys.ConnectionId] = reqArg.Id;
- 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 void 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());
- //send headersBuffer
- reqArg.Stream.Write (headersBuffer, 0, headersBuffer.Length);
- if (entityBuffer != null)
- reqArg.Stream.Write (entityBuffer, 0, entityBuffer.Length);
- }
- 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 "";
- }
- }
-
- static string[] knownHeaders = new string [] {
- "accept",
- "accept-charset",
- "accept-encoding",
- "authorization",
- "accept-language",
- "from",
- "host",
- "if-modified-since",
- "proxy-authorization",
- "range",
- "user-agent",
- "expect",
- "connection",
- "allow",
- "content-encoding",
- "content-language",
- "content-length",
- "content-range",
- "content-type",
- "content-version",
- "derived-from",
- "expires",
- "last-modified",
- "link",
- "title",
- "transfere-encoding",
- "url-header",
- "extension-header"
- };
- }
- }
|