最近在实现一个可视化数据解析工具,需要在Wpf程序中实现一个HttpServer,实现RESTfull接口,可以接收客户端(DTU或其他嵌入式设备)发送的请求。这种接口在ASP.NET中很容易实现,在Wpf程序中,需要有一个HttpServer才可以,开始考虑将Node.js签入到Wpf中,基本的功能可以实现,但是太麻烦了,程序的可移植性降低了,最好是在Wpf程序中内置这样的一个HttpServer。(当然,单纯的RESTful功能,通过WCF完全可以实现,此处的代码,给大家展示了在socket的基础上,时下基本的http协议,很有意思)
从CodeProject找到两篇文章:http://www.codeproject.com/Articles/137979/Simple-HTTP-Server-in-C 和 http://www.codeproject.com/Articles/25050/Embedded-NET-HTTP-Server ,参考
// Client-server helpers for TCP/IP // ClientInfo: wrapper for a socket which throws // Receive events, and allows for messaged communication (using // a specified character as end-of-message) // Server: simple TCP server that throws Connect events // ByteBuilder: utility class to manage byte arrays built up // in multiple transactions // (C) Richard Smith 2005-9 // [email protected] // You can use this for free and give it to people as much as you like // as long as you leave a credit to me :). // Code to connect to a SOCKS proxy modified from // http://www.thecodeproject.com/csharp/ZaSocks5Proxy.asp // changelog 1.6 // Option for thread synchronisation on a UI control // Define this symbol to include console output in various places //#define DEBUG // Define this symbol to use the old host name resolution //#define NET_1_1 using System; using System.IO; using System.Net; using System.Text; using System.Threading; using System.Collections; using System.Net.Sockets; using System.Windows.Forms; using System.Security.Cryptography; using RedCorona.Cryptography; //[assembly:System.Reflection.AssemblyVersion("1.6.2010.1228")] namespace RedCorona.Net { public delegate void ConnectionRead(ClientInfo ci, String text); public delegate void ConnectionClosed(ClientInfo ci); public delegate void ConnectionReadBytes(ClientInfo ci, byte[] bytes, int len); public delegate void ConnectionReadMessage(ClientInfo ci, uint code, byte[] bytes, int len); public delegate void ConnectionReadPartialMessage(ClientInfo ci, uint code, byte[] bytes, int start, int read, int sofar, int totallength); public delegate void ConnectionNotify(ClientInfo ci); public enum ClientDirection {In, Out, Left, Right, Both}; public enum MessageType {Unmessaged, EndMarker, Length, CodeAndLength}; // ServerDES: The server sends an encryption key on connect // ServerRSAClientDES: The server sends an RSA public key, the client sends back a key public enum EncryptionType {None, ServerKey, ServerRSAClientKey}; public class EncryptionUtils { static RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); public static byte[] GetRandomBytes(int length, bool addByte){ if(addByte && (length > 255)) throw new ArgumentException("Length must be 1 byte <256"); byte[] random = new byte[length + (addByte ? 1 : 0)]; rng.GetBytes(random); if(addByte) random[0] = (byte)length; return random; } } // OnReadBytes: catch the raw bytes as they arrive // OnRead: for text ended with a marker character // OnReadMessage: for binary info on a messaged client public class ClientInfo { internal Server server = null; private Socket sock; private String buffer; public event ConnectionRead OnRead; public event ConnectionClosed OnClose; public event ConnectionReadBytes OnReadBytes; public event ConnectionReadMessage OnReadMessage; public event ConnectionReadPartialMessage OnPartialMessage; public event ConnectionNotify OnReady; public MessageType MessageType; private ClientDirection dir; int id; bool alreadyclosed = false; public static int NextID = 100; private Exception closeException; private string closeReason = null; //private ClientThread t; public object Data = null; private Control threadSyncControl; // Encryption info EncryptionType encType; int encRead = 0, encStage, encExpected; internal bool encComplete; internal byte[] encKey; internal RSAParameters encParams; public EncryptionType EncryptionType { get { return encType; } set { if(encStage != 0) throw new ArgumentException("Key exchange has already begun"); encType = value; encComplete = encType == EncryptionType.None; encExpected = -1; } } public bool EncryptionReady { get { return encComplete; } } internal ICryptoTransform encryptor, decryptor; public ICryptoTransform Encryptor { get { return encryptor; } } public ICryptoTransform Decryptor { get { return decryptor; } } public Control ThreadSyncControl { get { return threadSyncControl; } set { threadSyncControl = value; } } private string delim; public const int BUFSIZE = 1024; byte[] buf = new byte[BUFSIZE]; ByteBuilder bytes = new ByteBuilder(10); byte[] msgheader = new byte[8]; byte headerread = 0; bool wantingChecksum = true; bool sentReady = false; public string Delimiter { get { return delim; } set { delim = value; } } public ClientDirection Direction { get { return dir; } } public Socket Socket { get { return sock; } } public Server Server { get { return server; } } public int ID { get { return id; } } public bool Closed { get { return !sock.Connected; } } public string CloseReason { get { return closeReason; } } public Exception CloseException { get { return closeException; } } public ClientInfo(Socket cl, bool StartNow) : this(cl, null, null, ClientDirection.Both, StartNow, EncryptionType.None) {} //public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d) : this(cl, read, readevt, d, true, EncryptionType.None) {} public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d, bool StartNow) : this(cl, read, readevt, d, StartNow, EncryptionType.None) {} public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d, bool StartNow, EncryptionType encryptionType){ sock = cl; buffer = ""; OnReadBytes = readevt; encType = encryptionType; encStage = 0; encComplete = encType == EncryptionType.None; OnRead = read; MessageType = MessageType.EndMarker; dir = d; delim = "\n"; id = NextID; // Assign each client an application-unique ID unchecked{NextID++;} //t = new ClientThread(this); if(StartNow) BeginReceive(); } public void BeginReceive(){ // t.t.Start(); if((encType == EncryptionType.None) && (!sentReady)) { sentReady = true; if(OnReady != null) OnReady(this); } sock.BeginReceive(buf, 0, BUFSIZE, 0, new AsyncCallback(ReadCallback), this); } public String Send(String text){ byte[] ba = Encoding.UTF8.GetBytes(text); String s = ""; for(int i = 0; i < ba.Length; i++) s += ba[i] + " "; Send(ba); return s; } public void SendMessage(uint code, byte[] bytes){ SendMessage(code, bytes, 0, bytes.Length); } public void SendMessage(uint code, byte[] bytes, byte paramType){ SendMessage(code, bytes, paramType, bytes.Length); } public void SendMessage(uint code, byte[] bytes, byte paramType, int len){ if(paramType > 0){ ByteBuilder b = new ByteBuilder(3); b.AddParameter(bytes, paramType); bytes = b.Read(0, b.Length); len = bytes.Length; } lock(sock){ byte checksum = 0; byte[] ba; switch(MessageType){ case MessageType.CodeAndLength: Send(ba = UintToBytes(code)); for(int i = 0; i < 4; i++) checksum += ba[i]; Send(ba = IntToBytes(len)); for(int i = 0; i < 4; i++) checksum += ba[i]; if(encType != EncryptionType.None) Send(new byte[]{checksum}); break; case MessageType.Length: Send(ba = IntToBytes(len)); for(int i = 0; i < 4; i++) checksum += ba[i]; if(encType != EncryptionType.None) Send(new byte[]{checksum}); break; } Send(bytes, len); if(encType != EncryptionType.None){ checksum = 0; for(int i = 0; i < len; i++) checksum += bytes[i]; Send(new byte[]{checksum}); } } } public void Send(byte[] bytes){ Send(bytes, bytes.Length); } public void Send(byte[] bytes, int len){ if(!encComplete) throw new IOException("Key exchange is not yet completed"); if(encType != EncryptionType.None){ byte[] outbytes = new byte[len]; Encryptor.TransformBlock(bytes, 0, len, outbytes, 0); bytes = outbytes; //Console.Write("Encryptor IV: "); LogBytes(encryptor.Key, encryptor.IV.length); } #if DEBUG Console.Write(ID + " Sent: "); LogBytes(bytes, len); #endif try { sock.Send(bytes, len, SocketFlags.None); } catch(Exception e){ closeException = e; closeReason = "Exception in send: "+e.Message; Close(); } } public bool MessageWaiting(){ FillBuffer(sock); return buffer.IndexOf(delim) >= 0; } public String Read(){ //FillBuffer(sock); int p = buffer.IndexOf(delim); if(p >= 0){ String res = buffer.Substring(0, p); buffer = buffer.Substring(p + delim.Length); return res; } else return ""; } private void FillBuffer(Socket sock){ byte[] buf = new byte[256]; int read; while(sock.Available != 0){ read = sock.Receive(buf); if(OnReadBytes != null) OnReadBytes(this, buf, read); buffer += Encoding.UTF8.GetString(buf, 0, read); } } void ReadCallback(IAsyncResult ar){ try { int read = sock.EndReceive(ar); //Console.WriteLine("Socket "+ID+" read "+read+" bytes"); if(read > 0){ DoRead(buf, read); BeginReceive(); } else { #if DEBUG Console.WriteLine(ID + " zero byte read closure"); #endif closeReason = "Zero byte read (no data available)"; closeException = null; Close(); } } catch(SocketException e){ #if DEBUG Console.WriteLine(ID + " socket exception closure: "+e); #endif closeReason = "Socket exception (" + e.Message + ")"; closeException = e; Close(); } catch(ObjectDisposedException e){ #if DEBUG Console.WriteLine(ID + " disposed exception closure"); #endif closeReason = "Disposed exception (socket object was disposed by the subsystem)"; closeException = e; Close(); } } internal void DoRead(byte[] buf, int read){ if(read > 0){ if(OnRead != null){ // Simple text mode buffer += Encoding.UTF8.GetString(buf, 0, read); while(buffer.IndexOf(delim) >= 0){ if(threadSyncControl != null) threadSyncControl.BeginInvoke(OnRead, new object[]{this, Read()}); else OnRead(this, Read()); } } } Console.WriteLine(ID + " read "+read+" bytes for event handler"); ReadInternal(buf, read, false); } public static void LogBytes(byte[] buf, int len){ byte[] ba = new byte[len]; Array.Copy(buf, ba, len); Console.WriteLine(ByteBuilder.FormatParameter(new Parameter(ba, ParameterType.Byte))); } void ReadInternal(byte[] buf, int read, bool alreadyEncrypted){ Console.WriteLine(ID + " read "+read+" bytes for event handler"); if((!alreadyEncrypted) && (encType != EncryptionType.None)){ if(encComplete){ #if DEBUG Console.Write(ID + " Received: "); LogBytes(buf, read); #endif buf = decryptor.TransformFinalBlock(buf, 0, read); #if DEBUG Console.Write(ID + " Decrypted: "); LogBytes(buf, read); #endif } else { // Client side key exchange int ofs = 0; if(encExpected < 0){ encStage++; ofs++; read--; encExpected = buf[0]; // length of key to come encKey = new byte[encExpected]; encRead = 0; } if(read >= encExpected){ Array.Copy(buf, ofs, encKey, encRead, encExpected); int togo = read - encExpected; encExpected = -1; #if DEBUG Console.WriteLine(ID + " Read encryption key: "+ByteBuilder.FormatParameter(new Parameter(encKey, ParameterType.Byte))); #endif if(server == null) ClientEncryptionTransferComplete(); else ServerEncryptionTransferComplete(); if(togo > 0){ byte[] newbuf = new byte[togo]; Array.Copy(buf, read + ofs - togo, newbuf, 0, togo); ReadInternal(newbuf, togo, false); } } else { Array.Copy(buf, ofs, encKey, encRead, read); encExpected -= read; encRead += read; } return; } } if((!alreadyEncrypted) && (OnReadBytes != null)){ if(threadSyncControl != null) threadSyncControl.BeginInvoke(OnReadBytes, new object[]{this, buf, read}); else OnReadBytes(this, buf, read); } if((OnReadMessage != null) && (MessageType != MessageType.Unmessaged)){ // Messaged mode int copied; uint code = 0; switch(MessageType){ case MessageType.CodeAndLength: case MessageType.Length: int length; if(MessageType == MessageType.Length){ copied = FillHeader(ref buf, 4, read); if(headerread < 4) break; length = GetInt(msgheader, 0, 4); } else{ copied = FillHeader(ref buf, 8, read); if(headerread < 8) break; code = (uint)GetInt(msgheader, 0, 4); length = GetInt(msgheader, 4, 4); } if(read == copied) break; // If encryption is on, the next byte is a checksum of the header int ofs = 0; if(wantingChecksum && (encType != EncryptionType.None)){ byte checksum = buf[0]; ofs++; wantingChecksum = false; byte headersum = 0; for(int i = 0; i < 8; i++) headersum += msgheader[i]; if(checksum != headersum){ Close(); throw new IOException("Header checksum failed! (was "+checksum+", calculated "+headersum+")"); } } bytes.Add(buf, ofs, read - ofs - copied); if(encType != EncryptionType.None) length++; // checksum byte // Now we know we are reading into the body of the message #if DEBUG Console.WriteLine(ID + " Added "+(read - ofs - copied)+" bytes, have "+bytes.Length+" of "+length); #endif if(OnPartialMessage != null) { if(threadSyncControl != null) threadSyncControl.BeginInvoke(OnPartialMessage, new object[]{this, code, buf, ofs, read - ofs - copied, bytes.Length, length}); else OnPartialMessage(this, code, buf, ofs, read - ofs - copied, bytes.Length, length); } if(bytes.Length >= length){ // A message was received! headerread = 0; wantingChecksum = true; byte[] msg = bytes.Read(0, length); if(encType != EncryptionType.None){ byte checksum = msg[length - 1], msgsum = 0; for(int i = 0; i < length - 1; i++) msgsum += msg[i]; if(checksum != msgsum){ Close(); throw new IOException("Content checksum failed! (was "+checksum+", calculated "+msgsum+")"); } if(threadSyncControl != null) threadSyncControl.BeginInvoke(OnReadMessage, new object[]{this, code, msg, length - 1}); else OnReadMessage(this, code, msg, length - 1); } else { if(threadSyncControl != null) threadSyncControl.BeginInvoke(OnReadMessage, new object[]{this, code, msg, length }); else OnReadMessage(this, code, msg, length); } // Don't forget to put the rest through the mill int togo = bytes.Length - length; if(togo > 0){ byte[] whatsleft = bytes.Read(length, togo); bytes.Clear(); ReadInternalSecondPass(whatsleft); } else bytes.Clear(); } //if(OnStatus != null) OnStatus(this, bytes.Length, length); break; } } } void ReadInternalSecondPass(byte[] newbytes){ ReadInternal(newbytes, newbytes.Length, true); } int FillHeader(ref byte[] buf, int to, int read) { int copied = 0; if(headerread < to){ // First copy the header into the header variable. for(int i = 0; (i < read) && (headerread < to); i++, headerread++, copied++){ msgheader[headerread] = buf[i]; } } if(copied > 0){ // Take the header bytes off the 'message' section byte[] newbuf = new byte[read - copied]; for(int i = 0; i < newbuf.Length; i++) newbuf[i] = buf[i + copied]; buf = newbuf; } return copied; } internal ICryptoTransform MakeEncryptor(){ return MakeCrypto(true); } internal ICryptoTransform MakeDecryptor(){ return MakeCrypto(false); } internal ICryptoTransform MakeCrypto(bool encrypt){ if(encrypt) return new SimpleEncryptor(encKey); else return new SimpleDecryptor(encKey); } void ServerEncryptionTransferComplete(){ switch(encType){ case EncryptionType.None: throw new ArgumentException("Should not have key exchange for unencrypted connection!"); case EncryptionType.ServerKey: throw new ArgumentException("Should not have server-side key exchange for server keyed connection!"); case EncryptionType.ServerRSAClientKey: // Symmetric key is in RSA-encoded encKey RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.ImportParameters(encParams); encKey = rsa.Decrypt(encKey, false); #if DEBUG Console.WriteLine("Symmetric key is: "); LogBytes(encKey, encKey.Length); #endif MakeEncoders(); server.KeyExchangeComplete(this); break; } } void ClientEncryptionTransferComplete(){ // A part of the key exchange process has been completed, and the key is // in encKey switch(encType){ case EncryptionType.None: throw new ArgumentException("Should not have key exchange for unencrypted connection!"); case EncryptionType.ServerKey: // key for transfer is now in encKey, so all is good MakeEncoders(); break; case EncryptionType.ServerRSAClientKey: // Stage 1: modulus; Stage 2: exponent // When the exponent arrives, create a random DES key // and send it switch(encStage){ case 1: encParams.Modulus = encKey; break; case 2: encParams.Exponent = encKey; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.ImportParameters(encParams); encKey = EncryptionUtils.GetRandomBytes(24, false); byte[] send = GetLengthEncodedVector(rsa.Encrypt(encKey, false)); sock.Send(send); #if DEBUG Console.WriteLine("Sent symmetric key: "+ByteBuilder.FormatParameter(new Parameter(send, ParameterType.Byte))); #endif MakeEncoders(); break; } break; } } internal void MakeEncoders(){ encryptor = MakeEncryptor(); decryptor = MakeDecryptor(); encComplete = true; if(OnReady != null) OnReady(this); } public static byte[] GetLengthEncodedVector(byte[] from){ int l = from.Length; if(l > 255) throw new ArgumentException("Cannot length encode more than 255"); byte[] to = new byte[l + 1]; to[0] = (byte)l; Array.Copy(from, 0, to, 1, l); return to; } public static int GetInt(byte[] ba, int from, int len){ int r = 0; for(int i = 0; i < len; i++) r += ba[from + i] << ((len - i - 1) * 8); return r; } public static int[] GetIntArray(byte[] ba){ return GetIntArray(ba, 0, ba.Length, 4); } public static int[] GetIntArray(byte[] ba, int from, int len){ return GetIntArray(ba, from, len, 4); } public static int[] GetIntArray(byte[] ba, int from, int len, int stride){ int[] res = new int[len / stride]; for(int i = 0; i < res.Length; i++){ res[i] = GetInt(ba, from + (i * stride), stride); } return res; } public static uint[] GetUintArray(byte[] ba){ uint[] res = new uint[ba.Length / 4]; for(int i = 0; i < res.Length; i++){ res[i] = (uint)GetInt(ba, i * 4, 4); } return res; } public static double[] GetDoubleArray(byte[] ba){ double[] res = new double[ba.Length / 8]; for(int i = 0; i < res.Length; i++){ res[i] = BitConverter.ToDouble(ba, i * 8); } return res; } public static byte[] IntToBytes(int val){ return UintToBytes((uint)val); } public static byte[] UintToBytes(uint val){ byte[] res = new byte[4]; for(int i = 3; i >= 0; i--){ res[i] = (byte)val; val >>= 8; } return res; } public static byte[] IntArrayToBytes(int[] val){ byte[] res = new byte[val.Length * 4]; for(int i = 0; i < val.Length; i++){ byte[] vb = IntToBytes(val[i]); res[(i * 4)] = vb[0]; res[(i * 4) + 1] = vb[1]; res[(i * 4) + 2] = vb[2]; res[(i * 4) + 3] = vb[3]; } return res; } public static byte[] UintArrayToBytes(uint[] val){ byte[] res = new byte[val.Length * 4]; for(uint i = 0; i < val.Length; i++){ byte[] vb = IntToBytes((int)val[i]); res[(i * 4)] = vb[0]; res[(i * 4) + 1] = vb[1]; res[(i * 4) + 2] = vb[2]; res[(i * 4) + 3] = vb[3]; } return res; } public static byte[] DoubleArrayToBytes(double[] val){ byte[] res = new byte[val.Length * 8]; for(int i = 0; i < val.Length; i++){ byte[] vb = BitConverter.GetBytes(val[i]); for(int ii = 0; ii < 8; ii++) res[(i*8)+ii] = vb[ii]; } return res; } public static byte[] StringArrayToBytes(string[] val, Encoding e){ byte[][] baa = new byte[val.Length][]; int l = 0; for(int i = 0; i < val.Length; i++){ baa[i] = e.GetBytes(val[i]); l += 4 + baa[i].Length; } byte[] r = new byte[l + 4]; IntToBytes(val.Length).CopyTo(r, 0); int ofs = 4; for(int i = 0; i < baa.Length; i++){ IntToBytes(baa[i].Length).CopyTo(r, ofs); ofs += 4; baa[i].CopyTo(r, ofs); ofs += baa[i].Length; } return r; } public static string[] GetStringArray(byte[] ba, Encoding e){ int l = GetInt(ba, 0, 4), ofs = 4; string[] r = new string[l]; for(int i = 0; i < l; i++){ int thislen = GetInt(ba, ofs, 4); ofs += 4; r[i] = e.GetString(ba, ofs, thislen); ofs += thislen; } return r; } public void Close(string reason){ if(!alreadyclosed){ closeReason = reason; Close(); } } public void Close(){ if(!alreadyclosed){ if(server != null) server.ClientClosed(this); if(OnClose != null) { if((threadSyncControl != null) && (threadSyncControl.InvokeRequired)) threadSyncControl.Invoke(OnClose, new object[]{this}); else OnClose(this); } alreadyclosed = true; #if DEBUG Console.WriteLine("**closed client** at "+DateTime.Now.Ticks); #endif } sock.Close(); } } public class Sockets { // Socks proxy inspired by http://www.thecodeproject.com/csharp/ZaSocks5Proxy.asp public static SocksProxy SocksProxy; public static bool UseSocks = false; public static Socket CreateTCPSocket(String address, int port){return CreateTCPSocket(address, port, UseSocks, SocksProxy);} public static Socket CreateTCPSocket(String address, int port, bool useSocks, SocksProxy proxy){ Socket sock; if(useSocks) sock = ConnectToSocksProxy(proxy.host, proxy.port, address, port, proxy.username, proxy.password); else { #if NET_1_1 IPAddress host = Dns.GetHostByName(address).AddressList[0]; #else IPAddress host = Dns.GetHostEntry(address).AddressList[0]; #endif sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sock.Connect(new IPEndPoint(host, port)); } return sock; } private static string[] errorMsgs= { "Operation completed successfully.", "General SOCKS server failure.", "Connection not allowed by ruleset.", "Network unreachable.", "Host unreachable.", "Connection refused.", "TTL expired.", "Command not supported.", "Address type not supported.", "Unknown error." }; public static Socket ConnectToSocksProxy(IPAddress proxyIP, int proxyPort, String destAddress, int destPort, string userName, string password){ byte[] request = new byte[257]; byte[] response = new byte[257]; ushort nIndex; IPAddress destIP = null; try{ destIP = IPAddress.Parse(destAddress); } catch { } IPEndPoint proxyEndPoint = new IPEndPoint(proxyIP, proxyPort); // open a TCP connection to SOCKS server... Socket s; s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); s.Connect(proxyEndPoint); /* } catch(SocketException){ throw new SocketException(0, "Could not connect to proxy server."); }*/ nIndex = 0; request[nIndex++]=0x05; // Version 5. request[nIndex++]=0x02; // 2 Authentication methods are in packet... request[nIndex++]=0x00; // NO AUTHENTICATION REQUIRED request[nIndex++]=0x02; // USERNAME/PASSWORD // Send the authentication negotiation request... s.Send(request,nIndex,SocketFlags.None); // Receive 2 byte response... int nGot = s.Receive(response,2,SocketFlags.None); if (nGot!=2) throw new ConnectionException("Bad response received from proxy server."); if (response[1]==0xFF) { // No authentication method was accepted close the socket. s.Close(); throw new ConnectionException("None of the authentication method was accepted by proxy server."); } byte[] rawBytes; if (/*response[1]==0x02*/true) {//Username/Password Authentication protocol nIndex = 0; request[nIndex++]=0x05; // Version 5. // add user name request[nIndex++]=(byte)userName.Length; rawBytes = Encoding.Default.GetBytes(userName); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; // add password request[nIndex++]=(byte)password.Length; rawBytes = Encoding.Default.GetBytes(password); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; // Send the Username/Password request s.Send(request,nIndex,SocketFlags.None); // Receive 2 byte response... nGot = s.Receive(response,2,SocketFlags.None); if (nGot!=2) throw new ConnectionException("Bad response received from proxy server."); if (response[1] != 0x00) throw new ConnectionException("Bad Usernaem/Password."); } // This version only supports connect command. // UDP and Bind are not supported. // Send connect request now... nIndex = 0; request[nIndex++]=0x05; // version 5. request[nIndex++]=0x01; // command = connect. request[nIndex++]=0x00; // Reserve = must be 0x00 if (destIP != null) {// Destination adress in an IP. switch(destIP.AddressFamily) { case AddressFamily.InterNetwork: // Address is IPV4 format request[nIndex++]=0x01; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; case AddressFamily.InterNetworkV6: // Address is IPV6 format request[nIndex++]=0x04; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; } } else {// Dest. address is domain name. request[nIndex++]=0x03; // Address is full-qualified domain name. request[nIndex++]=Convert.ToByte(destAddress.Length); // length of address. rawBytes = Encoding.Default.GetBytes(destAddress); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; } // using big-edian byte order byte[] portBytes = BitConverter.GetBytes((ushort)destPort); for (int i=portBytes.Length-1;i>=0;i--) request[nIndex++]=portBytes[i]; // send connect request. s.Send(request,nIndex,SocketFlags.None); s.Receive(response); // Get variable length response... if (response[1]!=0x00) throw new ConnectionException(errorMsgs[response[1]]); // Success Connected... return s; } } public struct SocksProxy { public IPAddress host; public ushort port; public string username, password; public SocksProxy(String hostname, ushort port, String username, String password){ this.port = port; #if NET_1_1 host = Dns.GetHostByName(hostname).AddressList[0]; #else host = Dns.GetHostEntry(hostname).AddressList[0]; #endif this.username = username; this.password = password; } } public class ConnectionException: Exception { public ConnectionException(string message) : base(message) {} } // Server code cribbed from Framework Help public delegate bool ClientEvent(Server serv, ClientInfo new_client); // whether to accept the client public class Server { class ClientState { // To hold the state information about a client between transactions internal Socket Socket = null; internal const int BufferSize = 1024; internal byte[] buffer = new byte[BufferSize]; internal StringBuilder sofar = new StringBuilder(); internal ClientState(Socket sock){ Socket = sock; } } Hashtable clients = new Hashtable(); Socket ss; public event ClientEvent Connect, ClientReady; public IEnumerable Clients { get { return clients.Values; } } public Socket ServerSocket { get { return ss; } } public ClientInfo this[int id]{ get { return (ClientInfo)clients[id]; } /* foreach(ClientInfo ci in Clients) if(ci.ID == id) return ci; return null; }*/ } public object SyncRoot { get { return this; } } private EncryptionType encType; public EncryptionType DefaultEncryptionType { get { return encType; } set { encType = value; } } public int Port { get { return ((IPEndPoint)ss.LocalEndPoint).Port; } } public Server(int port) : this(port, null) {} public Server(int port, ClientEvent connDel) { Connect = connDel; ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp ); ss.Bind(new IPEndPoint(IPAddress.Any, port)); ss.Listen(100); // Start the accept process. When a connection is accepted, the callback // must do this again to accept another connection ss.BeginAccept(new AsyncCallback(AcceptCallback), ss); } internal void ClientClosed(ClientInfo ci){ lock(SyncRoot) clients.Remove(ci.ID); } public void Broadcast(byte[] bytes){ lock(SyncRoot) foreach(ClientInfo ci in Clients) ci.Send(bytes); } public void BroadcastMessage(uint code, byte[] bytes){BroadcastMessage(code, bytes, 0); } public void BroadcastMessage(uint code, byte[] bytes, byte paramType){ lock(SyncRoot) foreach(ClientInfo ci in Clients) ci.SendMessage(code, bytes, paramType); } // ASYNC CALLBACK CODE void AcceptCallback(IAsyncResult ar) { try{ Socket server = (Socket) ar.AsyncState; Socket cs = server.EndAccept(ar); // Start the thing listening again server.BeginAccept(new AsyncCallback(AcceptCallback), server); ClientInfo c = new ClientInfo(cs, null, null, ClientDirection.Both, false); c.server = this; // Allow the new client to be rejected by the application if(Connect != null){ if(!Connect(this, c)){ // Rejected cs.Close(); return; } } // Initiate key exchange c.EncryptionType = encType; switch(encType){ case EncryptionType.None: KeyExchangeComplete(c); break; case EncryptionType.ServerKey: c.encKey = GetSymmetricKey(); byte[] key = ClientInfo.GetLengthEncodedVector(c.encKey); cs.Send(key); #if DEBUG Console.Write(c.ID + " Sent key: "); ClientInfo.LogBytes(key, key.Length); #endif c.MakeEncoders(); KeyExchangeComplete(c); break; case EncryptionType.ServerRSAClientKey: RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); RSAParameters p = rsa.ExportParameters(true); cs.Send(ClientInfo.GetLengthEncodedVector(p.Modulus)); cs.Send(ClientInfo.GetLengthEncodedVector(p.Exponent)); c.encParams = p; break; default: throw new ArgumentException("Unknown or unsupported encryption type "+encType); } lock(SyncRoot) clients[c.ID] = c; c.BeginReceive(); } catch(ObjectDisposedException) { } catch(SocketException) { } catch(Exception e) { Console.WriteLine(e); } } protected virtual byte[] GetSymmetricKey(){ return EncryptionUtils.GetRandomBytes(24, false); } internal void KeyExchangeComplete(ClientInfo ci){ // Key exchange is complete on this client. Client ready // handlers may still force a close of the connection if(ClientReady != null) if(!ClientReady(this, ci)) ci.Close("ClientReady callback rejected connection"); } ~Server() { Close(); } public void Close(){ ArrayList cl2 = new ArrayList(); foreach(ClientInfo c in Clients) cl2.Add(c); foreach(ClientInfo c in cl2) c.Close("Server shutdown"); ss.Close(); } } public class ByteBuilder : MarshalByRefObject { byte[][] data; int packsize, used, paraminx = 0; public int Length { get { int len = 0; for(int i = 0; i < used; i++) len += data[i].Length; return len; } } public byte this[int i]{ get { return Read(i, 1)[0]; } } public ByteBuilder() : this(10) {} public ByteBuilder(int packsize){ this.packsize = packsize; used = 0; data = new byte[packsize][]; } public ByteBuilder(byte[] data) { packsize = 1; used = 1; this.data = new byte[][] { data }; } public ByteBuilder(byte[] data, int len) : this(data, 0, len) {} public ByteBuilder(byte[] data, int from, int len) : this(1) { Add(data, from, len); } public void Add(byte[] moredata){ Add(moredata, 0, moredata.Length); } public void Add(byte[] moredata, int from, int len){ //Console.WriteLine("Getting "+from+" to "+(from+len-1)+" of "+moredata.Length); if(used < packsize){ data[used] = new byte[len]; for(int j = from; j < from + len; j++) data[used][j - from] = moredata[j]; used++; } else { // Compress the existing items into the first array byte[] newdata = new byte[Length + len]; int np = 0; for(int i = 0; i < used; i++) for(int j = 0; j < data[i].Length; j++) newdata[np++] = data[i][j]; for(int j = from; j < from + len; j++) newdata[np++] = moredata[j]; data[0] = newdata; for(int i = 1; i < used; i++) data[i] = null; used = 1; } } public byte[] Read(int from, int len){ if(len == 0) return new byte[0]; byte[] res = new byte[len]; int done = 0, start = 0; for(int i = 0; i < used; i++){ if((start + data[i].Length) <= from){ start += data[i].Length; continue; } // Now we're in the data block for(int j = 0; j < data[i].Length; j++){ if((j + start) < from) continue; res[done++] = data[i][j]; if(done == len) return res; } } throw new ArgumentException("Datapoints "+from+" and "+(from+len)+" must be less than "+Length); } public void Clear(){ used = 0; for(int i = 0; i < used; i++) data[i] = null; } public Parameter GetNextParameter(){ return GetParameter(ref paraminx); } public void ResetParameterPointer(){ paraminx = 0; } public Parameter GetParameter(ref int index){ paraminx = index; Parameter res = new Parameter(); res.Type = Read(index++, 1)[0]; byte[] lenba = Read(index, 4); index += 4; int len = ClientInfo.GetInt(lenba, 0, 4); res.content = Read(index, len); index += len; return res; } public void AddParameter(Parameter param){ AddParameter(param.content, param.Type); } public void AddParameter(byte[] content, byte Type){ Add(new byte[]{Type}); Add(ClientInfo.IntToBytes(content.Length)); Add(content); } public void AddInt(int i){ AddParameter(ClientInfo.IntToBytes(i), ParameterType.Int); } public void AddIntArray(int[] ia){ AddParameter(ClientInfo.IntArrayToBytes(ia), ParameterType.Int); } public void AddString(string s){ AddParameter(Encoding.UTF8.GetBytes(s), ParameterType.String); } public void AddStringArray(string[] sa){ AddParameter(ClientInfo.StringArrayToBytes(sa, Encoding.UTF8), ParameterType.StringArray); } public void AddDouble(double i){ AddParameter(BitConverter.GetBytes(i), ParameterType.Double); } public void AddLong(long i){ AddParameter(BitConverter.GetBytes(i), ParameterType.Long); } public void AddDoubleArray(double[] ia){ AddParameter(ClientInfo.DoubleArrayToBytes(ia), ParameterType.Double); } public int GetInt(){ return ClientInfo.GetInt(GetNextParameter().content, 0, 4);} public int[] GetIntArray(){ return ClientInfo.GetIntArray(GetNextParameter().content);} public double GetDouble(){ return BitConverter.ToDouble(GetNextParameter().content, 0);} public double[] GetDoubleArray(){ return ClientInfo.GetDoubleArray(GetNextParameter().content);} public long GetLong(){ return BitConverter.ToInt64(GetNextParameter().content, 0);} public string GetString(){ return Encoding.UTF8.GetString(GetNextParameter().content);} public string[] GetStringArray(){ return ClientInfo.GetStringArray(GetNextParameter().content, Encoding.UTF8);} public static String FormatParameter(Parameter p){ switch(p.Type){ case ParameterType.Int: int[] ia = ClientInfo.GetIntArray(p.content); StringBuilder sb = new StringBuilder(); foreach(int i in ia) sb.Append(i + " "); return sb.ToString(); case ParameterType.Uint: ia = ClientInfo.GetIntArray(p.content); sb = new StringBuilder(); foreach(int i in ia) sb.Append(i.ToString("X8") + " "); return sb.ToString(); case ParameterType.Double: double[] da = ClientInfo.GetDoubleArray(p.content); sb = new StringBuilder(); foreach(double d in da) sb.Append(d + " "); return sb.ToString(); case ParameterType.String: return Encoding.UTF8.GetString(p.content); case ParameterType.StringArray: string[] sa = ClientInfo.GetStringArray(p.content, Encoding.UTF8); sb = new StringBuilder(); foreach(string s in sa) sb.Append(s + "; "); return sb.ToString(); case ParameterType.Byte: sb = new StringBuilder(); foreach(int b in p.content) sb.Append(b.ToString("X2") + " "); return sb.ToString(); default: return "??"; } } } [Serializable] public struct Parameter { public byte Type; public byte[] content; public Parameter(byte[] content, byte type){ this.content = content; Type = type; } } public struct ParameterType { public const byte Unparameterised = 0; public const byte Int = 1; public const byte Uint = 2; public const byte String = 3; public const byte Byte = 4; public const byte StringArray = 5; public const byte Double = 6; public const byte Long = 7; } } namespace RedCorona.Cryptography { // Cryptographic classes public abstract class BaseCrypto : ICryptoTransform { public int InputBlockSize { get { return 1; } } public int OutputBlockSize { get { return 1; } } public bool CanTransformMultipleBlocks { get { return true; } } public bool CanReuseTransform { get { return true; } } protected byte[] key; protected byte currentKey; protected int done = 0, keyinx = 0; protected BaseCrypto(byte[] key){ if(key.Length == 0) throw new ArgumentException("Must provide a key"); this.key = key; currentKey = 0; for(int i = 0; i < key.Length; i++) currentKey += key[i]; } protected abstract byte DoByte(byte b); public int TransformBlock(byte[] from, int frominx, int len, byte[] to, int toinx){ #if DEBUG Console.WriteLine("Starting transform, key is "+currentKey); #endif for(int i = 0; i < len; i++){ byte oldkey = currentKey; to[toinx + i] = DoByte(from[frominx + i]); #if DEBUG Console.WriteLine(" encrypting "+from[frominx + i]+" to "+to[toinx + i]+", key is "+oldkey); #endif BumpKey(); } return len; } public byte[] TransformFinalBlock(byte[] from, int frominx, int len){ byte[] to = new byte[len]; TransformBlock(from, frominx, len, to, 0); return to; } protected void BumpKey(){ keyinx = (keyinx + 1) % key.Length; currentKey = Multiply257(key[keyinx], currentKey); } protected static byte Multiply257(byte a, byte b){ return (byte)((((a + 1) * (b + 1)) % 257) - 1); } protected static byte[] complements = {0,128,85,192,102,42,146,224,199,179,186,149,177,201,119,240,120,99,229,89,48,221,189,74,71,88,237,100,194,59,198,248,147,188,234,49,131,114,144,44,162,152,5,110,39,94,174,165,20,35,125,172,96,118,242,178,247,225,60,29,58,227,101,252,86,73,233,222,148,245,180,24,168,65,23,185,246,200,243,150,164,209,95,204,126,2,64,183,25,19,208,175,151,215,45,82,52,138,134,17,27,62,4,214,163,176,244,187,223,249,43,217,115,123,37,112,133,158,53,14,16,157,139,113,219,50,84,254,1,171,205,36,142,116,98,239,241,202,97,122,143,218,132,140,38,212,6,32,68,11,79,92,41,251,193,228,238,121,117,203,173,210,40,104,80,47,236,230,72,191,253,129,51,160,46,91,105,12,55,9,70,232,190,87,231,75,10,107,33,22,182,169,3,154,28,197,226,195,30,8,77,13,137,159,83,130,220,235,90,81,161,216,145,250,103,93,211,111,141,124,206,21,67,108,7,57,196,61,155,18,167,184,181,66,34,207,166,26,156,135,15,136,54,78,106,69,76,56,31,109,213,153,63,170,127,255}; protected static byte Complement257(byte b){ return complements[b]; } public void Dispose(){} // for IDisposable } public class SimpleEncryptor : BaseCrypto { public SimpleEncryptor(byte[] key) : base(key) {} protected override byte DoByte(byte b){ byte b2 = Multiply257((byte)(b+currentKey), currentKey); currentKey = Multiply257((byte)(b+b2), currentKey); return b2; } } public class SimpleDecryptor : BaseCrypto { public SimpleDecryptor(byte[] key) : base(key) {} protected override byte DoByte(byte b){ byte b2 = (byte)(Multiply257(b, Complement257(currentKey)) - currentKey); currentKey = Multiply257((byte)(b+b2), currentKey); return b2; } } }
HTTP.cs内容如下:
//#define DEBUG // Embedded HTTP server, partial 1.1 support with keep-alive and session management // REQUIRES: Sockets.dll (or Sockets.cs) // v1.0 // HttpServer: Main class that implements a HTTP server on top of Sockets server. // Includes session management via cookies, keep-alive, UTF8 transfer of text // and simple query string parsing (via GET or POST). // HttpRequest: Represents the request made by the user, with header fields, // query string and cookies pre-parsed. Typical server code will read the // properties of this request to determine what to do. // HttpResponse: The response which is to be sent back to the user. A simple // server will set the Content property, but you can send binary information // via RawContent, and change the mime type or response code. // Session: A container into which you may put state information that will be // available in future requests from the same user. // IHttpHandler: You must implement this interface in order to process // requests. // SubstitutingFileHandler: An implementation of IHttpHandler that reads files // from disk and allows substitutions of <%pseudotags> within text documents. // (C) Richard Smith 2008 // [email protected] // If downloaded from CodeProject, this file is subject to the CodeProject Open Licence 1.0. // If downloaded from elsewhere, you may freely distribute the source code, as long as this // header is not removed or modified. You may not charge for the source code or any compiled // library that includes this class; however you may link to it from commercial software. Please // leave a credit to the original download location in your documentation or About box. // Simple HTTP server using System; using System.IO; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Collections; using System.Collections.Generic; namespace RedCorona.Net { public class HttpServer { Server s; Hashtable hostmap = new Hashtable(); // Map<string, string>: Host => Home folder ArrayList handlers = new ArrayList(); // List<IHttpHandler> Hashtable sessions = new Hashtable(); // Map<string,Session> int sessionTimeout = 600; public Hashtable Hostmap { get { return hostmap; } } public Server Server { get { return s; } } public ArrayList Handlers { get { return handlers; } } public int SessionTimeout { get { return sessionTimeout; } set { sessionTimeout = value; CleanUpSessions(); } } public HttpServer(Server s) { this.s = s; s.Connect += new ClientEvent(ClientConnect); handlers.Add(new FallbackHandler()); } bool ClientConnect(Server s, ClientInfo ci) { ci.Delimiter = "\r\n\r\n"; ci.Data = new ClientData(ci); ci.OnRead += new ConnectionRead(ClientRead); ci.OnReadBytes += new ConnectionReadBytes(ClientReadBytes); return true; } void ClientRead(ClientInfo ci, string text) { // Read header, if in right state ClientData data = (ClientData)ci.Data; if (data.state != ClientState.Header) return; // already done; must be some text in content, which will be handled elsewhere text = text.Substring(data.headerskip); Console.WriteLine("Read header: " + text + " (skipping first " + data.headerskip + ")"); data.headerskip = 0; string[] lines = text.Replace("\r\n", "\n").Split('\n'); data.req.HeaderText = text; // First line: METHOD /path/url HTTP/version string[] firstline = lines[0].Split(' '); if (firstline.Length != 3) { SendResponse(ci, data.req, new HttpResponse(400, "Incorrect first header line " + lines[0]), true); return; } if (firstline[2].Substring(0, 4) != "HTTP") { SendResponse(ci, data.req, new HttpResponse(400, "Unknown protocol " + firstline[2]), true); return; } data.req.Method = firstline[0]; data.req.Url = firstline[1]; data.req.HttpVersion = firstline[2].Substring(5); int p; for (int i = 1; i < lines.Length; i++) { p = lines[i].IndexOf(':'); if (p > 0) data.req.Header[lines[i].Substring(0, p)] = lines[i].Substring(p + 2); else Console.WriteLine("Warning, incorrect header line " + lines[i]); } // If ? in URL, split out query information p = firstline[1].IndexOf('?'); if (p > 0) { data.req.Page = data.req.Url.Substring(0, p); data.req.QueryString = data.req.Url.Substring(p + 1); } else { data.req.Page = data.req.Url; data.req.QueryString = ""; } if (data.req.Page.IndexOf("..") >= 0) { SendResponse(ci, data.req, new HttpResponse(400, "Invalid path"), true); return; } if (!data.req.Header.TryGetValue("Host", out data.req.Host)) { SendResponse(ci, data.req, new HttpResponse(400, "No Host specified"), true); return; } string cookieHeader; if (data.req.Header.TryGetValue("Cookie", out cookieHeader)) { string[] cookies = cookieHeader.Split(';'); foreach (string cookie in cookies) { p = cookie.IndexOf('='); if (p > 0) { data.req.Cookies[cookie.Substring(0, p).Trim()] = cookie.Substring(p + 1); } else { data.req.Cookies[cookie.Trim()] = ""; } } } string contentLengthString; if (data.req.Header.TryGetValue("Content-Length", out contentLengthString)) data.req.ContentLength = Int32.Parse(contentLengthString); else data.req.ContentLength = 0; //if(data.req.ContentLength > 0){ data.state = ClientState.PreContent; data.skip = text.Length + 4; //} else DoProcess(ci); //ClientReadBytes(ci, new byte[0], 0); // For content length 0 body } public string GetFilename(HttpRequest req) { string folder = (string)hostmap[req.Host]; if (folder == null) folder = "webhome"; if (req.Page == "/") return folder + "/index.html"; else return folder + req.Page; } void DoProcess(ClientInfo ci) { ClientData data = (ClientData)ci.Data; string sessid; if (data.req.Cookies.TryGetValue("_sessid", out sessid)) data.req.Session = (Session)sessions[sessid]; bool closed = Process(ci, data.req); data.state = closed ? ClientState.Closed : ClientState.Header; data.read = 0; HttpRequest oldreq = data.req; data.req = new HttpRequest(); // Once processed, the connection will be used for a new request data.req.Session = oldreq.Session; // ... but session is persisted data.req.From = ((IPEndPoint)ci.Socket.RemoteEndPoint).Address; } void ClientReadBytes(ClientInfo ci, byte[] bytes, int len) { CleanUpSessions(); int ofs = 0; ClientData data = (ClientData)ci.Data; Console.WriteLine("Reading " + len + " bytes of content, in state " + data.state + ", skipping " + data.skip + ", read " + data.read); switch (data.state) { case ClientState.Content: break; case ClientState.PreContent: data.state = ClientState.Content; if ((data.skip - data.read) > len) { data.skip -= len; return; } ofs = data.skip - data.read; data.skip = 0; break; //case ClientState.Header: data.read += len - data.headerskip; return; default: data.read += len; return; } data.req.Content += Encoding.Default.GetString(bytes, ofs, len - ofs); data.req.BytesRead += len - ofs; data.headerskip += len - ofs; #if DEBUG Console.WriteLine("Reading " + (len - ofs) + " bytes of content. Got " + data.req.BytesRead + " of " + data.req.ContentLength); #endif if (data.req.BytesRead >= data.req.ContentLength) { if (data.req.Method == "POST") { if (data.req.QueryString == "") data.req.QueryString = data.req.Content; else data.req.QueryString += "&" + data.req.Content; } ParseQuery(data.req); DoProcess(ci); } } void ParseQuery(HttpRequest req) { if (req.QueryString == "") return; string[] sections = req.QueryString.Split('&'); for (int i = 0; i < sections.Length; i++) { int p = sections[i].IndexOf('='); if (p < 0) req.Query[sections[i]] = ""; else req.Query[sections[i].Substring(0, p)] = URLDecode(sections[i].Substring(p + 1)); } } public static string URLDecode(string input) { StringBuilder output = new StringBuilder(); int p; while ((p = input.IndexOf('%')) >= 0) { output.Append(input.Substring(0, p)); string hexNumber = input.Substring(p + 1, 2); input = input.Substring(p + 3); output.Append((char)int.Parse(hexNumber, System.Globalization.NumberStyles.HexNumber)); } return output.Append(input).ToString(); } protected virtual bool Process(ClientInfo ci, HttpRequest req) { HttpResponse resp = new HttpResponse(); resp.Url = req.Url; //注意,此处从最后添加的Handler开始遍历,如果找到合适的则退出循环 for (int i = handlers.Count - 1; i >= 0; i--) { IHttpHandler handler = (IHttpHandler)handlers[i]; //如果handler.Process返回成功,则直接退出循环 if (handler.Process(this, req, resp)) { //SendResponse(ci, req, resp, resp.ReturnCode != 200); SendResponse(ci, req, resp, true); return resp.ReturnCode != 200; } } return true; } enum ClientState { Closed, Header, PreContent, Content }; class ClientData { internal HttpRequest req = new HttpRequest(); internal ClientState state = ClientState.Header; internal int skip, read, headerskip; internal ClientData(ClientInfo ci) { req.From = ((IPEndPoint)ci.Socket.RemoteEndPoint).Address; } } public Session RequestSession(HttpRequest req) { if (req.Session != null) { if (sessions[req.Session.ID] == req.Session) return req.Session; } req.Session = new Session(req.From); sessions[req.Session.ID] = req.Session; return req.Session; } void CleanUpSessions() { ICollection keys = sessions.Keys; ArrayList toRemove = new ArrayList(); foreach (string k in keys) { Session s = (Session)sessions[k]; int time = (int)((DateTime.Now - s.LastTouched).TotalSeconds); if (time > sessionTimeout) { toRemove.Add(k); Console.WriteLine("Removed session " + k); } } foreach (object k in toRemove) sessions.Remove(k); } // Response stuff static Hashtable Responses = new Hashtable(); static HttpServer() { Responses[200] = "OK"; Responses[302] = "Found"; Responses[303] = "See Other"; Responses[400] = "Bad Request"; Responses[404] = "Not Found"; Responses[500] = "Misc Server Error"; Responses[502] = "Server Busy"; } void SendResponse(ClientInfo ci, HttpRequest req, HttpResponse resp, bool close) { #if DEBUG Console.WriteLine("Response: " + resp.ReturnCode + Responses[resp.ReturnCode]); #endif ByteBuilder bb = new ByteBuilder(); bb.Add(Encoding.UTF8.GetBytes("HTTP/1.1 " + resp.ReturnCode + " " + Responses[resp.ReturnCode] + "\r\nDate: " + DateTime.Now.ToString("R") + "\r\nServer: RedCoronaEmbedded/1.0" + "\r\nConnection: " + (close ? "close" : "Keep-Alive"))); if (resp.RawContent == null) bb.Add(Encoding.UTF8.GetBytes("\r\nContent-Encoding: utf-8" + "\r\nContent-Length: " + resp.Content.Length)); else bb.Add(Encoding.UTF8.GetBytes("\r\nContent-Length: " + resp.RawContent.Length)); if (resp.ContentType != null) bb.Add(Encoding.UTF8.GetBytes("\r\nContent-Type: " + resp.ContentType)); if (req.Session != null) bb.Add(Encoding.UTF8.GetBytes("\r\nSet-Cookie: _sessid=" + req.Session.ID + "; path=/")); foreach (KeyValuePair<string, string> de in resp.Header) bb.Add(Encoding.UTF8.GetBytes("\r\n" + de.Key + ": " + de.Value)); bb.Add(Encoding.UTF8.GetBytes("\r\n\r\n")); // End of header if (resp.RawContent != null) bb.Add(resp.RawContent); else bb.Add(Encoding.UTF8.GetBytes(resp.Content)); ci.Send(bb.Read(0, bb.Length)); #if DEBUG Console.WriteLine("** SENDING\n" + resp.Content); #endif if (close) ci.Close(); } class FallbackHandler : IHttpHandler { public bool Process(HttpServer server, HttpRequest req, HttpResponse resp) { #if DEBUG Console.WriteLine("Processing " + req); #endif server.RequestSession(req); StringBuilder sb = new StringBuilder(); sb.Append("<h3>Session</h3>"); sb.Append("<p>ID: " + req.Session.ID + "<br>User: " + req.Session.User); sb.Append("<h3>Header</h3>"); sb.Append("Method: " + req.Method + "; URL: '" + req.Url + "'; HTTP version " + req.HttpVersion + "<p>"); foreach (KeyValuePair<string, string> ide in req.Header) sb.Append(" " + ide.Key + ": " + ide.Value + "<br>"); sb.Append("<h3>Cookies</h3>"); foreach (KeyValuePair<string, string> ide in req.Cookies) sb.Append(" " + ide.Key + ": " + ide.Value + "<br>"); sb.Append("<h3>Query</h3>"); foreach (KeyValuePair<string, string> ide in req.Query) sb.Append(" " + ide.Key + ": " + ide.Value + "<br>"); sb.Append("<h3>Content</h3>"); sb.Append(req.Content); resp.Content = sb.ToString(); return true; } } } public class HttpRequest { public bool GotHeader = false; public string Method, Url, Page, HttpVersion, Host, Content, HeaderText, QueryString; public IPAddress From; //public byte[] RawContent; public Dictionary<string, string> Query = new Dictionary<string, string>(), Header = new Dictionary<string, string>(), Cookies = new Dictionary<string, string>(); public int ContentLength, BytesRead; public Session Session; } public class HttpResponse { public int ReturnCode = 200; public Dictionary<string, string> Header = new Dictionary<string, string>(); public string Url, Content, ContentType = "text/html"; public byte[] RawContent = null; public HttpResponse() { } public HttpResponse(int code, string content) { ReturnCode = code; Content = content; } public void MakeRedirect(string newurl) { ReturnCode = 303; Header["Location"] = newurl; Content = "This document is requesting a redirection to <a href=" + newurl + ">" + newurl + "</a>"; } } public interface IHttpHandler { bool Process(HttpServer server, HttpRequest request, HttpResponse response); } public class Session { string id; IPAddress user; DateTime lasttouched; Hashtable data = new Hashtable(); public string ID { get { return id; } } public DateTime LastTouched { get { return lasttouched; } } public IPAddress User { get { return user; } } public object this[object key] { get { return data[key]; } set { data[key] = value; Touch(); } } public Session(IPAddress user) { this.user = user; this.id = Guid.NewGuid().ToString(); Touch(); } public void Touch() { lasttouched = DateTime.Now; } } public class SubstitutingFileReader : IHttpHandler { // Reads a file, and substitutes <%x> HttpRequest req; bool substitute = true; public bool Substitute { get { return substitute; } set { substitute = value; } } public static Hashtable MimeTypes; static SubstitutingFileReader() { MimeTypes = new Hashtable(); MimeTypes[".html"] = "text/html"; MimeTypes[".htm"] = "text/html"; MimeTypes[".css"] = "text/css"; MimeTypes[".js"] = "application/x-javascript"; MimeTypes[".png"] = "image/png"; MimeTypes[".gif"] = "image/gif"; MimeTypes[".jpg"] = "image/jpeg"; MimeTypes[".jpeg"] = "image/jpeg"; } public virtual bool Process(HttpServer server, HttpRequest request, HttpResponse response) { string fn = server.GetFilename(request); if (!File.Exists(fn)) { response.ReturnCode = 404; response.Content = "File not found."; return true; } string ext = Path.GetExtension(fn); string mime = (string)MimeTypes[ext]; if (mime == null) mime = "application/octet-stream"; response.ContentType = mime; try { if (mime.Substring(0, 5) == "text/") { // Mime type 'text' is substituted StreamReader sr = new StreamReader(fn); response.Content = sr.ReadToEnd(); sr.Close(); if (substitute) { // Do substitutions Regex regex = new Regex(@"\<\%(?<tag>[^>]+)\>"); lock (this) { req = request; response.Content = regex.Replace(response.Content, new MatchEvaluator(RegexMatch)); } } } else { FileStream fs = File.Open(fn, FileMode.Open); byte[] buf = new byte[fs.Length]; fs.Read(buf, 0, buf.Length); fs.Close(); response.RawContent = buf; } } catch (Exception e) { response.ReturnCode = 500; response.Content = "Error reading file: " + e; return true; } return true; } public virtual string GetValue(HttpRequest req, string tag) { return "<span class=error>Unknown substitution: " + tag + "</span>"; } string RegexMatch(Match m) { try { return GetValue(req, m.Groups["tag"].Value); } catch (Exception e) { return "<span class=error>Error substituting " + m.Groups["tag"].Value + "</span>"; } } } public enum HMethod { GET, POST, DELETE, PUT } /// <summary> /// RESTfull处理器的接口 /// </summary> public interface IRESTfulHandler { bool Process(HttpServer server, HttpRequest request, HttpResponse response, Dictionary<string, string> param); } public class RESTfulApiHandlerBase : IHttpHandler { public RESTfulApiHandlerBase(HMethod method, string sUrl, List<string> param, IRESTfulHandler handler) { m_method = method; m_sUrl = sUrl; m_listParam = param; m_handler = handler; } private HMethod m_method = HMethod.GET; private string m_sUrl = ""; private List<string> m_listParam = new List<string>(); private IRESTfulHandler m_handler = null; public bool Process(HttpServer server, HttpRequest request, HttpResponse response) { if (request.Method != m_method.ToString()) return false; if (!request.Page.Equals(m_sUrl, StringComparison.CurrentCultureIgnoreCase)) return false; //处理查询参数 string sQueryString = request.QueryString; if (m_method == HMethod.DELETE || m_method == HMethod.PUT) sQueryString = request.Content; Dictionary<string, string> queryParam = GetParam(sQueryString); if (queryParam.Count != m_listParam.Count) return false; HashSet<string> set1 = new HashSet<string>(m_listParam); HashSet<string> set2 = new HashSet<string>(queryParam.Keys); //两个集合是否相同 if (!set1.SetEquals(set2)) return false; if (m_handler != null) return m_handler.Process(server, request, response, queryParam); return false; } private static Dictionary<String, String> GetParam(String sQueryString) { Dictionary<String, String> param = new Dictionary<string, string>(); if (string.IsNullOrEmpty(sQueryString)) return param; string[] ar = sQueryString.Split(new char[] { '&' }); foreach (string item in ar) { int nFind = item.IndexOf('='); string sKey = item.Substring(0, nFind); string sValue = item.Substring(nFind + 1, item.Length - nFind - 1); param[sKey] = sValue; } return param; } } }
将Program.cs中代码修改如下:
using RedCorona.Net; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { HttpServer http = new HttpServer(new Server(800)); http.Handlers.Add(new SubstitutingFileReader()); http.Handlers.Add(new RESTfulApiHandlerBase(HMethod.GET, "/api/patientinfo", new List<string>() { "Function", "UserJID" }, new PatientGetHander())); http.Handlers.Add(new RESTfulApiHandlerBase(HMethod.POST, "/api/patientinfo", new List<string>() { "Function", "UserJID" }, new PatientPostHander())); http.Handlers.Add(new RESTfulApiHandlerBase(HMethod.DELETE, "/api/patientinfo", new List<string>() { "Function", "UserJID" }, new PatientDeleteHander())); http.Handlers.Add(new RESTfulApiHandlerBase(HMethod.PUT, "/api/patientinfo", new List<string>() { "Function", "UserJID" }, new PatientPutHander())); Console.WriteLine("服务已启动,点击任何按键退出"); Console.ReadKey(); } } public class PatientGetHander : IRESTfulHandler { public bool Process(HttpServer server, HttpRequest request, HttpResponse response, Dictionary<string, string> param) { Console.WriteLine("PatientGetHander 完成!"); response.Content = "PatientGetHander 完成!"; return true; } } public class PatientDeleteHander : IRESTfulHandler { public bool Process(HttpServer server, HttpRequest request, HttpResponse response, Dictionary<string, string> param) { Console.WriteLine("PatientDeleteHander 完成!"); response.Content = "PatientDeleteHander 完成!"; return true; } } public class PatientPostHander : IRESTfulHandler { public bool Process(HttpServer server, HttpRequest request, HttpResponse response, Dictionary<string, string> param) { Console.WriteLine("PatientPostHander 完成!"); response.Content = "PatientPostHander 完成!"; return true; } } public class PatientPutHander : IRESTfulHandler { public bool Process(HttpServer server, HttpRequest request, HttpResponse response, Dictionary<string, string> param) { Console.WriteLine("PatientPutHander 完成!"); response.Content = "PatientPutHander 完成!"; return true; } } }
运行应用程序,可以发送POST/GET/PUT/DELETE命令进行测试。