using System;
using System.Text;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace WebServer
{
	/// <summary>
	/// MyWebServer类
	/// </summary>
	public class MyWebServer : System.Windows.Forms.Form
	{
		private TcpListener listener;
		private int port= 80 ; // 服务器监听的端口
		private Thread processor; 
		private Thread clientservice;
		private Socket socket;
		private System.Windows.Forms.ListBox lbStatus;
		private System.ComponentModel.Container components = null;
		
		
		/// <summary>
		/// 构造器
		/// </summary>
		public MyWebServer()
		{
			InitializeComponent();
			
					
		}



		public void Start()
		{
			processor = new Thread(new ThreadStart(StartListening));
			processor.Start();
		}

	
		
		public void Suspend()
		{
			processor.Suspend();
		}

		
	
		public void Stop()
		{
			processor.Resume();
		}
			

		
		private void StartListening()
		{
			try
			{
				//在指定端口开始监听
				listener = new TcpListener(port);
				listener.Start();
				lbStatus.Items.Add("Web 服务器开始启动......");
				while (true) 
				{
					try
					{
						Socket socket = listener.AcceptSocket();
						clientservice = new Thread(new ThreadStart(ServiceClient));
						clientservice.Start();
					}
					catch(Exception e)
					{
						Console.WriteLine(e.ToString() );
					}
				}
				//开始新的线程
				
			}
			catch(SocketException se)
			{
				MessageBox.Show("在监听时出错:"+se.Message+"\r\n可能的错误:\r\n  端口80被占用.\r\n  解决方法:关闭使用端口80的程序.");
			}

		}

		

		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}


		
		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.lbStatus = new System.Windows.Forms.ListBox();
			this.SuspendLayout();
			// 
			// lbStatus
			// 
			this.lbStatus.Dock = System.Windows.Forms.DockStyle.Fill;
			this.lbStatus.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.lbStatus.ItemHeight = 14;
			this.lbStatus.Name = "lbStatus";
			this.lbStatus.ScrollAlwaysVisible = true;
			this.lbStatus.Size = new System.Drawing.Size(432, 270);
			this.lbStatus.TabIndex = 0;
			// 
			// MyWebServer
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
			this.ClientSize = new System.Drawing.Size(432, 273);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.lbStatus});
			this.Name = "MyWebServer";
			this.Text = "Web 服务器";
			this.ResumeLayout(false);

		}
		#endregion
		
		
		private static void Main() 
		{
			Application.Run(new MyWebServer());
		}
  
		

		/// <summary>
		/// Returns The Default File Name
		/// Input : WebServerRoot Folder
		/// Output: Default File Name
		/// </summary>
		/// <param name="sMyWebServerRoot"></param>
		/// <returns></returns>
		public string GetTheDefaultFileName(string sLocalDirectory)
		{
			StreamReader sr;
			String sLine = "";

			try
			{
				//打开default.dat文件，取得缺省文件名 
				sr = new StreamReader("data\\Default.Dat");

				while ((sLine = sr.ReadLine()) != null)
				{
					//在Web服务器的根目录下寻找缺省文件
					if (File.Exists( sLocalDirectory + sLine) == true)
						break;
				}
			}
			catch(Exception e)
			{
				MessageBox.Show("在设置默认文档时出错 : " + e.ToString());
			}
			if (File.Exists( sLocalDirectory + sLine) == true)
				return sLine;
			else
				return "";
		}



		/// <summary>
		/// This function takes FileName as Input and returns the mime type..
		/// </summary>
		/// <param name="sRequestedFile">To indentify the Mime Type</param>
		/// <returns>Mime Type</returns>
		public string GetMimeType(string sRequestedFile)
		{
			StreamReader sr;
			String sLine = "";
			String sMimeType = "";
			String sFileExt = "";
			String sMimeExt = "";
			
			// 转化为小写字符
			sRequestedFile = sRequestedFile.ToLower();
			
			int iStartPos = sRequestedFile.IndexOf(".");

			sFileExt = sRequestedFile.Substring(iStartPos);
			
			try
			{
				//打开Vdirs.dat文件，列出虚拟目录
				sr = new StreamReader("data\\Mime.Dat");

				while ((sLine = sr.ReadLine()) != null)
				{

					sLine.Trim();

					if (sLine.Length > 0)
					{
						//“；”为分隔符
						iStartPos = sLine.IndexOf(";");
						
						// 转化为小写
						sLine = sLine.ToLower();
						
						sMimeExt = sLine.Substring(0,iStartPos);
						sMimeType = sLine.Substring(iStartPos + 1);
						
						if (sMimeExt == sFileExt)
							break;
					}
				}
			}
			catch (Exception e)
			{
				lbStatus.Items.Add("An Exception Occurred : " + e.ToString());
			}

			if (sMimeExt == sFileExt)
				return sMimeType; 
			else
				return "";
		}



		/// <summary>
		/// 返回物理路径
		/// </summary>
		/// <param name="sMyWebServerRoot">Web Server Root Directory</param>
		/// <param name="sDirName">Virtual Directory </param>
		/// <returns>Physical local Path</returns>
		public string GetLocalPath(string sMyWebServerRoot, string sDirName)
		{

			StreamReader sr;
			String sLine = "";
			String sVirtualDir = ""; 
			String sRealDir = "";
			int iStartPos = 0;
			
	
			//去掉两边的空格
			sDirName.Trim();
			
			
			
			// 转化为小写
			sMyWebServerRoot = sMyWebServerRoot.ToLower();
			
			// 转化为小写
			sDirName = sDirName.ToLower();

			try
			{
				//Open the Vdirs.dat to find out the list virtual directories
				sr = new StreamReader("data\\VDirs.Dat");

				while ((sLine = sr.ReadLine()) != null)
				{
					//去掉多余的空格
					sLine.Trim();

					if (sLine.Length > 0)
					{
						//分隔符
						iStartPos = sLine.IndexOf(";");
						
						//转化为小写
						sLine = sLine.ToLower();
						
						sVirtualDir = sLine.Substring(0,iStartPos);
						sRealDir = sLine.Substring(iStartPos + 1);
						
						if (sVirtualDir == sDirName)
						{
							break;
						}
					}
				}
			}
			catch(Exception e)
			{
				lbStatus.Items.Add("An Exception Occurred : " + e.ToString());
			}


			lbStatus.Items.Add("Virtual Dir : " + sVirtualDir);
			lbStatus.Items.Add("Directory   : " + sDirName);
			lbStatus.Items.Add("Physical Dir: " + sRealDir);
			if (sVirtualDir == sDirName)
				return sRealDir;
			else
				return "";
		}
		


		/// <summary>
		/// 向客户端（浏览器）发送HTTP Header
		/// </summary>
		/// <param name="sHttpVersion">HTTP Version</param>
		/// <param name="sMIMEHeader">Mime Type</param>
		/// <param name="iTotBytes">Total Bytes to be sent in the body</param>
		/// <param name="socket">Socket reference</param>
		/// <returns></returns>
		public void SendHeader(string sHttpVersion, string sMIMEHeader, int iTotBytes, string sStatusCode, ref Socket socket)
		{

			String sBuffer = "";
			
			// 如果此种Mime类型不被支持则缺省为 text/html
			if (sMIMEHeader.Length == 0 )
			{
				sMIMEHeader = "text/html";  // 缺省 Mime 类型为 text/html
			}

			sBuffer = sBuffer + sHttpVersion + sStatusCode + "\r\n";
			sBuffer = sBuffer + "Server: cx1193719-b\r\n";
			sBuffer = sBuffer + "Content-Type: " + sMIMEHeader + "\r\n";
			sBuffer = sBuffer + "Accept-Ranges: bytes\r\n";
			sBuffer = sBuffer + "Content-Length: " + iTotBytes + "\r\n\r\n";
			
			Byte[] bSendData = Encoding.ASCII.GetBytes(sBuffer); 

			SendToBrowser( bSendData, ref socket);
		}



		/// <summary>
		/// 重载的方法, takes string, convert to bytes and calls 
		/// overloaded sendToBrowserFunction.
		/// </summary>
		/// <param name="sData">The data to be sent to the browser(client)</param>
		/// <param name="socket">Socket reference</param>
		public void SendToBrowser(String sData, ref Socket socket)
		{
			SendToBrowser (Encoding.ASCII.GetBytes(sData), ref socket);
		}



		/// <summary>
		/// Sends data to the browser (client)
		/// </summary>
		/// <param name="bSendData">Byte Array</param>
		/// <param name="socket">Socket reference</param>
		public void SendToBrowser(Byte[] bSendData, ref Socket socket)
		{
			int numBytes = 0;
			
			try
			{
				if (socket.Connected)
				{
					socket.Send(bSendData, bSendData.Length,0);
						
				}
				else
					lbStatus.Items.Add("Connection Dropped....");
			}
			catch (Exception  e)
			{
				MessageBox.Show("Error Occurred :" + e.Message  );
							
			}
		}


		//This method Accepts new connection and
		//First it receives the welcome massage from the client,
		//Then it sends the Current date time to the Client.
		public void ServiceClient()
		{

			int iStartPos = 0;
			String sRequest;
			String sDirName;
			String sRequestedFile;
			String sErrorMessage;
			String sLocalDir;
			String sMyWebServerRoot = "C:\\myPersonalWebServerRoot\\";
			String sPhysicalFilePath = "";
			String sFormattedMessage = "";
			String sResponse = "";
			
			bool keepalive = true;
			
			
				//接受一个新的连接
				
			if(socket.Connected)
			{
					
				//生成一个比特数组，来接收客户端的信息 
				Byte[] bReceive = new Byte[1024] ;
				int i = socket.Receive(bReceive,bReceive.Length,0) ;

				//比特流转化为字符串
				string sBuffer = Encoding.ASCII.GetString(bReceive);
	
				//目前我们只处理“GET”
				if (sBuffer.Substring(0,3) != "GET" )
				{
					lbStatus.Items.Add("Only Get Method is supported..");
					socket.Close();
					return;
				}

					
				// 寻找HTTP请求
				iStartPos = sBuffer.IndexOf("HTTP",1);


				// 取得HHTP和版本，例如它会返回 "HTTP/1.1"
				string sHttpVersion = sBuffer.Substring(iStartPos,8);
        
					        					
				// Extract the Requested Type and Requested file/directory
				sRequest = sBuffer.Substring(0,iStartPos - 1);
        
										
				//斜杠变成反斜杠
				sRequest.Replace("\\","/");


				//If file name is not supplied add forward slash to indicate 
				//that it is a directory and then we will look for the 
				//default file name..
				if ((sRequest.IndexOf(".") <1) && (!sRequest.EndsWith("/")))
				{
					sRequest = sRequest + "/"; 
				}

				//Extract the requested file name
				iStartPos = sRequest.LastIndexOf("/") + 1;
				sRequestedFile = sRequest.Substring(iStartPos);

					
				//Extract The directory Name
				sDirName = sRequest.Substring(sRequest.IndexOf("/"), sRequest.LastIndexOf("/")-3);
					
						
					
				/////////////////////////////////////////////////////////////////////
				// Identify the Physical Directory
				/////////////////////////////////////////////////////////////////////
				if ( sDirName == "/")
					sLocalDir = sMyWebServerRoot;
				else
				{
					//取得虚拟目录
					sLocalDir = GetLocalPath(sMyWebServerRoot, sDirName);
				}


					

				//如果物理路径不存在显示错误信息
				if (sLocalDir.Length == 0 )
				{
					sErrorMessage = "<H2>Error!! Requested Directory does not exists</H2><Br>";
					//sErrorMessage = sErrorMessage + "Please check data\\Vdirs.Dat";
					//格式化消息
					SendHeader(sHttpVersion,  "", sErrorMessage.Length, " 404 Not Found", ref socket);

					//发送到浏览器
					SendToBrowser(sErrorMessage, ref socket);

					socket.Close();
					
				}

					
				/////////////////////////////////////////////////////////////////////
				// 识别文件名
				/////////////////////////////////////////////////////////////////////

				//If The file name is not supplied then look in the default file list
				if (sRequestedFile.Length == 0 )
				{
					// 取得缺省文件名
					sRequestedFile = GetTheDefaultFileName(sLocalDir);

					if (sRequestedFile == "")
					{
						sErrorMessage = "<H2>Error!! No Default File Name Specified</H2>";
						SendHeader(sHttpVersion,  "", sErrorMessage.Length, " 404 Not Found", ref socket);
						SendToBrowser ( sErrorMessage, ref socket);

						socket.Close();

						return;
					}
				}

					


				/////////////////////////////////////////////////////////////////////
				// 取得 Mime 类型
				/////////////////////////////////////////////////////////////////////
					
				String sMimeType = GetMimeType(sRequestedFile);



				//生成物理路径
				sPhysicalFilePath = sLocalDir + sRequestedFile;
					
					
					
				if (File.Exists(sPhysicalFilePath) == false)
				{
				
					sErrorMessage = "<H2>404 Error! File Does Not Exists...</H2>";
					SendHeader(sHttpVersion, "", sErrorMessage.Length, " 404 Not Found", ref socket);
					SendToBrowser( sErrorMessage, ref socket);
					DisplayMessage(socket.RemoteEndPoint.ToString(),"GET",sPhysicalFilePath,"404");
				}
				else
				{
					int iTotBytes=0;

					sResponse ="";

					FileStream fs = new FileStream(sPhysicalFilePath, FileMode.Open, 	FileAccess.Read, FileShare.Read);
					// 生成一个BinaryReader，用于从文件中读取数据

						
					BinaryReader reader = new BinaryReader(fs);
					byte[] bytes = new byte[fs.Length];
					int read;
					while((read = reader.Read(bytes, 0, bytes.Length)) != 0) 
					{
						// 从文件中读取数据并发送出去
						sResponse = sResponse + Encoding.ASCII.GetString(bytes,0,read);

						iTotBytes = iTotBytes + read;

					}
					reader.Close(); 
					fs.Close();

					SendHeader(sHttpVersion,  sMimeType, iTotBytes, " 200 OK", ref socket);
					SendToBrowser(bytes, ref socket);
					DisplayMessage(socket.RemoteEndPoint.ToString(),"GET",sPhysicalFilePath,"200");
						
				}
				socket.Close();						
			}	
		}

	

		private void DisplayMessage(string IPAddr,string method,string reqFile,string returncode)
		{
			string strStatus="";
			strStatus=DateTime.Now.ToString();
			strStatus+=IPAddr;
			strStatus+=method;
			strStatus+=reqFile;
			strStatus+=returncode;
			lbStatus.Items.Add(strStatus) ;

		}

	}
}
