在.NET程序中实现HttpServer功能
在.NET程序中实现HttpServer功能
最近在实现一个可视化数据解析工具,需要在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 ,参考obJanova的文章(第二篇),对代码进行了改造,实现了自己需要的功能。
首先在Visual Studio中建一个控制台程序,添加两个文件,Sockets.cs和HTTP.cs,文件内容如下:
Sockets.cs文件内容:
// 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
// bobjanova@gmail.com
// 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
// bobjanova@gmail.com
// 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命令进行测试。
在.NET程序中实现HttpServer功能的更多相关文章
- HOW TO: 在 Visual C# .NET 应用程序中提供文件拖放功能
本文假定您熟悉下列主题: Windows 窗体列表框控件 Windows 窗体事件处理 生成示例的步骤 列表框控件提供了您需要处理的两个拖放事件: DragEnter 和 DragDrop. 当您在控 ...
- 微信小程序中悬浮窗功能的实现(主要探讨和解决在原生组件上的拖动)
问题场景 所谓悬浮窗就是图中微信图标的按钮,采用fixed定位,可拖动和点击. 这算是一个比较常见的实现场景了. 为什么要用cover-view做悬浮窗?原生组件出来背锅了~ 最初我做悬浮窗用的不是c ...
- 小程序中内容审核功能的使用(后台使用thinkPHP5.1)
本文包含文本和图片的检测 //接收要检测的文本内容并调用检测方法 public function textCheck(Request $request){ //内容安全识别 $data['conten ...
- Fastreport使用经验(转)在Delphi程序中访问报表对象
Fastreport使用经验(转) 在Delphi程序中访问报表对象 最基本的方法就是frxReport1.FindObject. 然后把返回的对象强制转换成它的类型,当然,在报表中必须真的有这么个东 ...
- 在DevExpress程序中使用GridView直接录入数据的时候,增加列表选择的功能
在我上篇随笔<在DevExpress程序中使用Winform分页控件直接录入数据并保存>中介绍了在GridView以及在其封装的分页控件上做数据的直接录入的处理,介绍情况下数据的保存和校验 ...
- Shepherd – 在应用程序中轻松实现引导功能
Shepherd 是一个指导用户使用应用程序的 JavaScript 库.它使用 Tether——另一个开源库,实现所有的步骤.Tether 确保你的步骤不会溢出屏幕或被剪裁.你可以很容易地指导用户使 ...
- MVC应用程序中,怎样控制与复制相同的功能
先看此篇<MVC程序实现Autocomplete功能> http://www.cnblogs.com/insus/p/3546255.html 它是实现使用jQuery实现文本框输入文字, ...
- VC++编程中为程序加入启动画面功能
如何为自己的程序加入启动画面 观察我们平常使用的软件,当我们双击软件的时候,会在主界面出现前,先行出现一个启动画面,由于前一阵子写了一个基于对话框的程序,亲自实验了下,今天就为大家简单的介绍下,在我 ...
- 在ASP.NET 5应用程序中的跨域请求功能详解
在ASP.NET 5应用程序中的跨域请求功能详解 浏览器安全阻止了一个网页中向另外一个域提交请求,这个限制叫做同域策咯(same-origin policy),这组织了一个恶意网站从另外一个网站读取敏 ...
随机推荐
- JavaWeb项目自动部署,持续集成
来公司以后,学会两种JavaWeb项目,自动部署. 1.jenkins持续集成.自动化部署 (1)安装jenkins----------推荐nginx跳转方式,以域名方式 (2)nginx采用不同域名 ...
- Django的Hello World
Django安装 yum -y install python #安装python yum -y install epel-release #安装扩展源 yum -y install python-pi ...
- css标准文档流
css标准文档流 所谓的标准文档流指的是网页当中的一个渲染顺序,就如同人类读书一样,从上向下,从左向右.网页的渲染顺序也是如此.而我们使用的标签默认都是存在于标准文档流当中. 标准文档流当中的特性 空 ...
- python学习日记(数据结构习题)
元素分类 有如下值li= [11,22,33,44,55,66,77,88,99,90],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中.即: {'k1' ...
- [国家集训队]整数的lqp拆分
我们的目标是求$\sum\prod_{i=1}^m F_{a_i}$ 设$f(i) = \sum\prod_{j=1}^i F_{a_j}$那么$f(i - 1) = \sum\prod_{j=1}^ ...
- electron打包之真的恶心
用electron-packager进行打包 这个模块的文档写的真的垃圾 1.先看看首页的参数介绍 就是说必选参数就是源码路径和app名字和--platform还有--arch咯,而且源码路径也没说是 ...
- [NOI2016]优秀的拆分&&BZOJ2119股市的预测
[NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...
- bzoj3427小P的牧场(斜率优化dp)
小P在MC里有n个牧场,自西向东呈一字形排列(自西向东用1…n编号),于是他就烦恼了:为了控制这n个牧场,他需要在某些牧场上面建立控制站,每个牧场上只能建立一个控制站,每个控制站控制的牧场是它所在的牧 ...
- centos7破解安装fisheye和Crucible
背景介绍: Atlassian的东西相信大家都不陌生,JIRA.Confluence……虽然说这些产品都要收费,也可以申请试用: FishEye 可以方便地查看代码,而Crucible 则是进行Cod ...
- Codeforces 1051E. Vasya and Big Integers
题意:给你N个点M条边,M-N<=20,有1e5个询问,询问两点的最短距离.保证没有自环和重边. 题解:连题目都在提示你这个20很有用,所以如果是颗树的话那任意两点的最短距离就是求一下lca搞一 ...