2004-12-17 Lluis Sanchez Gual <lluis@ximian.com>
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Http / HttpServer.cs
index e4d2c2c5f84c0a3d5a08e445ed64d02f460f72ac..3c60866951857254dbd97cae7c37c7cc5496bbd5 100644 (file)
 //             Hussein  Mehanna        hussein_mehanna@hotmail.com
 //
 //==========================================================================
+
+//
+// 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;
@@ -28,203 +49,166 @@ using System.Runtime.Remoting.Messaging;
 namespace System.Runtime.Remoting.Channels.Http
 {
 
-       class RequestArguments
+       internal class RequestArguments
        {
-               public RequestArguments(Socket SOCKET, HttpServerTransportSink SNK)
+               public RequestArguments (Socket socket, HttpServerTransportSink sink)
                {
-                       socket = SOCKET;
-                       snk = SNK;
+                       NetworkStream ns = new NetworkStream (socket);
+                       InputStream = ns;
+                       OutputStream = ns;
+                       Sink = sink;
                }
-               public Socket socket;
-               public HttpServerTransportSink snk;
+               
+               public Stream InputStream;
+               public Stream OutputStream;
+               public HttpServerTransportSink Sink;
        }
 
        internal sealed class HttpServer
        {
-               public int port;
-               private TcpListener listener=null;
-               private const int nTimeOut = 1000;
-               
-               public HttpServer(int port)
+               public static void ProcessRequest (object reqInfo)
                {
-                       try
-                       {
-                               listener = new TcpListener(port);
-                       }
-                       catch(NullReferenceException)
-                       {
-                               Console.WriteLine("The port is bound to another application");
-                       }
-               }
-
-               
-               public static void ProcessRequest(object Object)
-               {
-                       
-                       if(Object as RequestArguments == null)
+                       if(reqInfo as RequestArguments == null)
                                return;
 
-
-                       Socket socket;
-                       HttpServerTransportSink snk;
-
-                       RequestArguments reqArg = (RequestArguments)Object;
+                       RequestArguments reqArg = (RequestArguments)reqInfo;
                
-                       socket = reqArg.socket;
-                       snk = reqArg.snk;
-                               
-                        if(!socket.Connected)
-                                       return;
-
-                               //Step (1) Start Reciceve the header
-                               ArrayList  Headers = RecieveHeader(socket);
-                               
+                       //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(socket,Headers,HeaderFields,CustomHeaders))
-                                       return;
-                       
-
+                       //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 (3)
-                           if(!CheckRequest(socket,HeaderFields,CustomHeaders))
+                       //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;
-
-
-                               //Step (4) Recieve the entity body
-                               byte [] buffer =new byte[(int)HeaderFields["content-length"]];
-                               if(!RecieveEntityBody(socket,buffer))
-                                       return ;
-                               
+                       }
+                       else
+                               buffer = new byte [0];
                                
-
-                               //Step (5)
-
-                           if(! SendRequestForChannel(socket,snk,HeaderFields,CustomHeaders,buffer))
-                                       return ;
-
-       
+                       //Step (5)
+                       SendRequestForChannel (reqArg, HeaderFields, CustomHeaders, buffer);
                }
 
-               private static ArrayList RecieveHeader(Socket socket)
+               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)
+                       while (!bLastLine)
                        { 
                                //recieve line by line 
                                index = 0;
                                bEndOfLine = false;
 
                                //Step (1) is it an empty line?
-                               socket.Receive(buffer,index,1,SocketFlags.None);
+                               ist.Read (buffer, index, 1);
                                
                                if(buffer[index++]==13)
                                {
-                                       socket.Receive(buffer,index,1,SocketFlags.None);
+                                       ist.Read (buffer, index, 1);
                                        bLastLine=true;
                                        bEndOfLine = true;
                                }
                                
                                //Step (2) recieve line bytes
-                               while(!bEndOfLine)
+                               while (!bEndOfLine)
                                {
-                                       socket.Receive(buffer,index,1,SocketFlags.None);
+                                       ist.Read (buffer, index, 1);
 
-                                       if(buffer[index++]==13)
+                                       if(buffer [index++]==13)
                                        {
                                                bEndOfLine = true;
-                                               socket.Receive(buffer,index,1,SocketFlags.None);
+                                               ist.Read (buffer,index,1);
                                        }
-                                       
                                }
 
                                //Step (3) convert bytes to a string
-                               if(bLastLine)
+                               if (bLastLine)
                                        continue;
-                       Headers.Add( Encoding.ASCII.GetString(buffer,0,index));
-
-                               
+                                       
+                Headers.Add (Encoding.ASCII.GetString (buffer,0,index));
 
                        }//end while loop
                        
                        return Headers;
                }
                
-               private static bool ParseHeader(Socket socket, ArrayList Headers,IDictionary HeaderFields, IDictionary CustomHeaders)
+               private static bool ParseHeader (RequestArguments reqArg, ArrayList Headers, IDictionary HeaderFields, IDictionary CustomHeaders)
                {
-
-                       for(int i=0;i<Headers.Count;i++)
+                       for (int i=0;i<Headers.Count;i++)
                        {
-
-                               if( ReqMessageParser.ParseHeaderField((string)Headers[i],HeaderFields))
+                               if (ReqMessageParser.ParseHeaderField ((string)Headers[i],HeaderFields))
                                        continue;
-                               if(!ReqMessageParser.IsCustomHeader((string)Headers[i],CustomHeaders ) )
+                                       
+                               if (!ReqMessageParser.IsCustomHeader((string)Headers[i],CustomHeaders ) )
                                {
-                                       SendResponse(socket,400,null,null);
+                                       SendResponse (reqArg, 400, null, null);
                                        return false;
                                }
-                               
                        }
 
                        return true;
                }
 
-               
-
-               private static bool CheckRequest(Socket socket,IDictionary HeaderFields , IDictionary CustomHeaders)
+               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")
-                       {
-                               SendResponse(socket,501,null,null);
-                return false;
-                       }
+                       if (temp != "POST")
+                return true;
 
                        //Check for the content-length field
-                       if(HeaderFields["content-length"]==null)
+                       if (HeaderFields["content-length"]==null)
                        {
-                               SendResponse(socket,411,null,null);
+                               SendResponse (reqArg, 411, null, null);
                                return false;
                        }
                        return true;
                }
 
                
-               private static bool RecieveEntityBody(Socket socket, byte[] buffer)
+               private static bool RecieveEntityBody (RequestArguments reqArg, byte[] buffer)
                {
-                       
-
                        try
                        {
-                               //Recieved = socket.Receive(buffer,0,buffer.Length,SocketFlags.None);
                                int nr = 0;
                                while (nr < buffer.Length)
-                                       nr += socket.Receive (buffer, nr, buffer.Length - nr,SocketFlags.None);
+                                       nr += reqArg.InputStream.Read (buffer, nr, buffer.Length - nr);
                        }
-                       catch(SocketException e)
+                       catch (SocketException e)
                        {
                                switch(e.ErrorCode)
                                {
                                        case 10060 : //TimeOut
-                                               SendResponse(socket,408,null,null);
+                                               SendResponse (reqArg, 408, null, null);
                                                break;
                                        default :
-                                               //<Exception>                                   
-                                               break;
+                                               throw e;
                                }
                                
                                return false;
@@ -233,37 +217,29 @@ namespace System.Runtime.Remoting.Channels.Http
                        return true;
                }
        
-               private static bool SendRequestForChannel(Socket socket ,HttpServerTransportSink snk ,IDictionary HeaderFields , IDictionary CustomHeaders, byte[]buffer)
+               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);
 
-                       //These two headers are sent to the Soap Formatter
-                       //IF the Soap formatter on mono will need them , then uncomment them.
-                       //THeaders["__ConnectionId"] = Int64.Parse("1");
-                       //THeaders["__IPAddress"]= ((IPEndPoint)socket.RemoteEndPoint).Address;
-
-                       THeaders["__RequestUri"] =FixURI((string)HeaderFields["request-url"]);
-                       THeaders["Content-Type"]=HeaderFields["content-type"];
-                       THeaders["__RequestVerb"]=HeaderFields["method"];
-                       THeaders["__HttpVersion"] = HeaderFields["http-version"];
-                       THeaders["User-Agent"] = HeaderFields["user-agent"];
-                       THeaders["Host"] = HeaderFields["host"];
+                       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();
                        }
 
-                       snk.ServiceRequest(socket,stream,THeaders);
+                       reqArg.Sink.ServiceRequest (reqArg, stream, THeaders);
                        return true;
                }
 
@@ -277,73 +253,66 @@ namespace System.Runtime.Remoting.Channels.Http
                        
                }
                
-               public static bool SendResponse(Socket socket, int HttpStatusCode, ITransportHeaders  headers , Stream responseStream)
+               public static void SendResponse (RequestArguments reqArg, int httpStatusCode, ITransportHeaders headers, Stream responseStream)
                {
                        byte [] headersBuffer = null;
                        byte [] entityBuffer = null;
 
-                       StringBuilder ResponseStr ;             
-                       String Reason = GetReasonPhrase(HttpStatusCode);
+                       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.1 " + HttpStatusCode.ToString() + " " + Reason + "\r\n" );
-                       if(headers!=null)
-                               foreach(DictionaryEntry entry in headers)
+                       responseStr = new StringBuilder ("HTTP/1.0 " + httpStatusCode + " " + reason + "\r\n" );
+                       
+                       if (headers != null)
+                       {
+                               foreach (DictionaryEntry entry in headers)
                                {
-                                       ResponseStr.Append(entry.Key.ToString()+": "+entry.Value.ToString()+"\r\n");
+                                       string key = entry.Key.ToString();
+                                       if (key != CommonTransportKeys.HttpStatusCode && key != CommonTransportKeys.HttpReasonPhrase)
+                                               responseStr.Append(key + ": " + entry.Value.ToString() + "\r\n");
                                }
+                       }
                        
-                       ResponseStr.Append("Server: MS .NET Remoting, MS .NET CLR 1.0.3705.0\r\n");
+                       responseStr.Append("Server: Mono Remoting, Mono CLR " + System.Environment.Version.ToString() + "\r\n");
 
-                       if(responseStream != null)
-                       if(responseStream.Length!=0)
+                       if(responseStream != null && responseStream.Length!=0)
                        {
-                               ResponseStr.Append("Content-Length: "+responseStream.Length.ToString()+"\r\n"); 
+                               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);
+                               responseStream.Read(entityBuffer,0,entityBuffer.Length);
                        }
+                       else
+                               responseStr.Append("Content-Length: 0\r\n"); 
 
-                       ResponseStr.Append("\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
-                               if(socket.Send(headersBuffer,0,headersBuffer.Length,SocketFlags.None) != headersBuffer.Length)
-                                       return false;
-
-
+                       headersBuffer = Encoding.ASCII.GetBytes (responseStr.ToString());
 
-                               if(entityBuffer != null)
-                                       if(socket.Send(entityBuffer,0,entityBuffer.Length,SocketFlags.None) != entityBuffer.Length)
-                                               return false;
+                       //send headersBuffer
+                       reqArg.OutputStream.Write (headersBuffer, 0, headersBuffer.Length);
 
-                       }
-                       catch (SocketException )
-                       {
-                               //<EXCEPTION>
-                               //may be its the client's fault so just return with false
-                               return false;
-                       }
-                
-                 return true;
-               }
-
-               public static bool SendResponse(Socket socket , int HttpStatusCode ,string ReasonPhrase, ITransportHeaders headers , Stream responseStream )
-               {
-                       
-                       return true;
+                       if (entityBuffer != null)
+                               reqArg.OutputStream.Write (entityBuffer, 0, entityBuffer.Length);
                }
 
-               private static string GetReasonPhrase(int HttpStatusCode)
+               internal static string GetReasonPhrase (int HttpStatusCode)
                {
                        switch (HttpStatusCode)
                        {
-                               case 100 : return " Continue" ;
+                               case 100 : return "Continue" ;
                                case 101  :return "Switching Protocols";
                                case 200  :return "OK";
                                case 201  :return "Created";
@@ -394,39 +363,34 @@ namespace System.Runtime.Remoting.Channels.Http
        }
        
        
-        internal  sealed class   ReqMessageParser
+       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];
+               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 ReqMessageParser ()
                {
-                       
-
                }
 
-                public static bool  ParseHeaderField(string buffer,IDictionary headers)
+                public static bool ParseHeaderField(string buffer,IDictionary headers)
                 {
-                       
                         try
                         {
-
                                 if(!bInitialized)
                                 {
                                         Initialize();
                                         bInitialized =true;
                                 }
-                        
 
                                 if(IsRequestField(buffer,headers))
                                         return true;
@@ -437,16 +401,12 @@ namespace System.Runtime.Remoting.Channels.Http
                         catch(Exception )
                         {
                                 //<Exception>
-
                         }
 
                         //Exception
 
                         return false;
-
                 }
-                 
-
                 
                 private static bool Initialize()
                 {