// 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;
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;
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;
}
}
- 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";
}
- 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;
catch(Exception )
{
//<Exception>
-
}
//Exception
return false;
-
}
-
-
private static bool Initialize()
{