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 !
Josh N
April 5, 2009, April 05, 2009 22:56, permalink
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(); } }
Josh N
April 5, 2009, April 05, 2009 22:58, permalink
ooops.. sorry missed your comment about WebRequest my bad..
teariok.myopenid.com
April 5, 2009, April 05, 2009 23:18, permalink
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.
Jackson Savitraz
June 3, 2009, June 03, 2009 17:57, permalink
Yes, it has!
Because HttpWebRequest/HttpWebResponse classes doesn´t permit to mask the Host header.
eBuster
August 14, 2009, August 14, 2009 14:24, permalink
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.
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.