Avatar

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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..)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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..

Avatar

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.

Avatar

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