55502f40dc8b7c769880b10874abc9d0

This is a HTTP socket that I use in one of my projects. It can't handle anything other than 200 OK, but for the purpose it was intended anything other than that should fail anyway, so I don't need proper HTTP response code handling. I'm just hoping really that if there is a way it could be tidied up, sped up or otherwise improved that someone can show how and then learning can happen. My particular problem is reading the header a byte at a time, I am sure there is a better way but such a method escapes me.

Oh and before you say it, I know I can just use WebRequest. If that is your only criticism, please keep it to yourself.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Net.Sockets;
using System.Collections;

namespace LPT_5.HTTPSocket
{
    class Socket
    {
        private System.Net.Sockets.Socket msSock = null;
        private String msHost = null;
        private int miPort = 80;
        private Hashtable mhHeaders=null;

        public Socket(String sHost, int iPort)
        {
            msHost = sHost;
            miPort = iPort;
            
            //Initialise Standard Headers
            mhHeaders = new Hashtable();
            AddHeader("Connection","Close"); //We wont need to do any pipelining
            AddHeader("Host", sHost);
        }

        public void AddHeader(String sKey, String sValue)
        {
            if(!mhHeaders.ContainsKey(sKey))
                mhHeaders.Add(sKey,sValue);
        }

        private void RemoveHeader(String sKey)
        {
            if(mhHeaders.ContainsKey(sKey))
                mhHeaders.Remove(sKey);
        }

        public String GetURL(String sURL)
        {
            msSock = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            msSock.Connect(msHost, miPort);
            String sReq = "GET " + sURL + " HTTP/1.1";
            sReq = TagHeaders(sReq);
            msSock.Send(System.Text.Encoding.ASCII.GetBytes(sReq));

            Hashtable htHeadParams = new Hashtable();
            int iResult = ReceiveHeader(ref htHeadParams);
            String sBody = null;
            if(htHeadParams.ContainsKey("Content-Length"))
            {
                int iLen = Convert.ToInt32((String)htHeadParams["Content-Length"],10);
                int iOffs = 0;
                byte[] bBuff = new byte[iLen];
                while (iOffs < iLen)
                {
                    iOffs += msSock.Receive(bBuff,iOffs,iLen-iOffs,SocketFlags.None);
                }
                sBody += System.Text.Encoding.ASCII.GetString(bBuff);
            }
            msSock.Close();

            return sBody;
        }

        public byte[] GetRaw(String sURL)
        {
            msSock = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            msSock.Connect(msHost, miPort);
            String sReq = "GET " + sURL + " HTTP/1.1";
            sReq = TagHeaders(sReq);
            msSock.Send(System.Text.Encoding.ASCII.GetBytes(sReq));

            Hashtable htHeadParams = new Hashtable();
            if(ReceiveHeader(ref htHeadParams) == 200) //If we got the data with no problems
            {
                int iLen = Convert.ToInt32((String)htHeadParams["Content-Length"], 10);
                byte[] bBuff = new byte[iLen];
                if (htHeadParams.ContainsKey("Content-Length"))
                {
                    int iOffs = 0;
                    while (iOffs < iLen)
                    {
                        iOffs += msSock.Receive(bBuff, iOffs, iLen - iOffs, SocketFlags.None);
                    }
                }
                msSock.Close();

                return bBuff;
            }
            return null;
        }

        private String TagHeaders(String sReq)
        {
            IDictionaryEnumerator deEnumer = mhHeaders.GetEnumerator();
            while(deEnumer.MoveNext())
            {
                sReq += "\r\n" + deEnumer.Key + ": " + deEnumer.Value;
            }
            return sReq + "\r\n\r\n";
        }

        private int ReceiveHeader(ref Hashtable htHash)
        {
            String sHead = null;
            byte[] buff = new byte[1];

            /*
             We dont know the size of the header, but we do know that header and
             body are separated by a double newline, so read a byte at a time until
             we receive double newline.
            */
            do
            {
                msSock.Receive(buff, 0, 1, SocketFlags.None);
                sHead += (char)buff[0];
            }
            while (!sHead.Contains("\r\n\r\n"));
            int iResult = ProcessHeaders(sHead,ref htHash);

            return iResult;
        }

        private int ProcessHeaders(String sHead, ref Hashtable htHash)
        {
            //Split wont work with \r\n, so just regex away the carriage return
            String sToSplit = Regex.Replace(sHead,"\r","");
            String[] sParams = sToSplit.Split('\n');

            foreach (String sItem in sParams)
            {
                String[] sKeyVal = sItem.Split(':');
                if(sKeyVal.Length >= 2)
                {
                    String sKey = sKeyVal[0].Trim();
                    String sVal = sKeyVal[1].Trim();
                    if (htHash.ContainsKey(sKey))
                        htHash[sKey] += ',' + sVal; //Group multiple headers
                    else
                        htHash.Add(sKey, sVal);
                }
            }

            String[] sResultParams = sParams[0].Split(' ');
            return Convert.ToInt32(sResultParams[1]); //Return the HTTP Response Code
        }
    }
}

Refactorings

No refactoring yet !

60888d58de3bf08cbc0c73e67fd6fd86

Josh N

April 5, 2009, April 05, 2009 22:56, permalink

No rating. Login to rate!

is there a reason for not using the built in HttpWebRequest/HttpWebResponse classes? (maybe im missing something..)

using System;
using System.Net;

public class App {

  static void Main() {
    HttpWebRequest  request  = WebRequest.Create("http://www.contoso.com/") as HttpWebRequest;
    HttpWebResponse response = request.GetResponse() as HttpWebResponse;

    Console.WriteLine(response.StatusCode);

    foreach (var key in response.Headers.AllKeys)
      Console.WriteLine("{0} = {1}", key, response.Headers[key]);
    
    response.Close();
  }
}
60888d58de3bf08cbc0c73e67fd6fd86

Josh N

April 5, 2009, April 05, 2009 22:58, permalink

No rating. Login to rate!

ooops.. sorry missed your comment about WebRequest my bad..

55502f40dc8b7c769880b10874abc9d0

teariok.myopenid.com

April 5, 2009, April 05, 2009 23:18, permalink

No rating. Login to rate!

It was simply a learning exercise. I haven't used C# a great deal and I enjoy this kind of programming. Plus for another project I will likely be using sockets for TCP communication using a custom protocol, so working on improving my socket code would help with that.

7e843dc28ccd806ac05ed7f374f3b398

Jackson Savitraz

June 3, 2009, June 03, 2009 17:57, permalink

No rating. Login to rate!

Yes, it has!
Because HttpWebRequest/HttpWebResponse classes doesn´t permit to mask the Host header.

D41d8cd98f00b204e9800998ecf8427e

eBuster

August 14, 2009, August 14, 2009 14:24, permalink

No rating. Login to rate!

Sockets also allow you to deal with pages that have a 404 in the headder like some eBay pages and a webrequest with throw an error even when the responce contains the page you are look for but a browser just thinks it's a custom error page and not an advert ! Good try eBay.

i'm looking for a faster way to read the sockets stream without doing it one byte at a time.

3aa63bb586c5c59f9fc6b2afb9417821

geeks

October 26, 2009, October 26, 2009 15:46, permalink

No rating. Login to rate!

Interesting,

This is some great help for me ,

Keep up the good work,

Thanks for writing, most people don't bother.

Your refactoring





Format Copy from initial code

or Cancel