using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading; using System.Web; namespace LJLib.HttpServer { public class LJHttpProcessor { private TcpClient socket; private LJHttpServer srv; private Stream inputStream; private StreamWriter outputStream; public string http_method; public string http_url; public string http_protocol_versionstring; private string querystr; //GET方式请求,路径后接的"?key=value……" private Dictionary httpHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); /// /// 显式参数列表 /// public Dictionary GETqueryParams() { var rslt = new Dictionary(); if (!string.IsNullOrEmpty(querystr)) { var kvs = querystr.Split('&'); foreach (var kv in kvs) { var index = kv.IndexOf('='); if (index > 0) { var key = kv.Substring(0, index); var value = kv.Substring(index + 1); rslt[key] = value; } } } return rslt; } /// /// 显式参数列表 /// public Dictionary POSTqueryParams(StreamReader inputData) { var rslt = new Dictionary(); if (inputData != null) { var rawData = inputData.ReadToEnd(); if (!string.IsNullOrEmpty(rawData)) { var kvs = rawData.Split('&'); foreach (var kv in kvs) { var index = kv.IndexOf('='); if (index > 0) { var key = kv.Substring(0, index); var value = HttpUtility.UrlDecode(kv.Substring(index + 1)); rslt[key] = value; } } } } return rslt; } private static int MAX_POST_SIZE = 10*1024*1024; // 10MB public LJHttpProcessor(TcpClient s, LJHttpServer srv) { socket = s; this.srv = srv; } private string streamReadLine(Stream inputStream) { string data = ""; while (true) { var next_char = inputStream.ReadByte(); if (next_char == '\n') { break; } if (next_char == '\r') { continue; } if (next_char == -1) { Thread.Sleep(10); continue; } ; data += Convert.ToChar(next_char); } return data; } public void Process() { // we can't use a StreamReader for input, because it buffers up extra data on us inside it's // "processed" view of the world, and we want the data raw after the headers inputStream = new BufferedStream(socket.GetStream()); // we probably shouldn't be using a streamwriter for all output from handlers either outputStream = new StreamWriter(new BufferedStream(socket.GetStream())); try { ParseRequest(); ReadHeaders(); if (http_method.Equals("GET")) { HandleGetRequest(); } else if (http_method.Equals("POST")) { HandlePostRequest(); } } catch (Exception e) { Console.WriteLine("Exception: " + e); WriteError("{\"ErrMsg\":\"" + e + "\"}"); } outputStream.Flush(); inputStream = null; outputStream = null; // bs = null; socket.Close(); } private void ParseRequest() { String request = streamReadLine(inputStream); string[] tokens = request.Split(' '); if (tokens.Length != 3) { throw new Exception("invalid http request line"); } http_method = tokens[0].ToUpper(); http_url = tokens[1]; if (http_url.IndexOf('?') > 0) { var qsIndex = http_url.IndexOf('?'); querystr = http_url.Substring(qsIndex + 1); http_url = http_url.Substring(0, qsIndex); } http_protocol_versionstring = tokens[2]; Console.WriteLine("starting: " + request); } private void ReadHeaders() { Console.WriteLine("readHeaders()"); String line; while ((line = streamReadLine(inputStream)) != null) { if (line.Equals("")) { Console.WriteLine("got headers"); return; } int separator = line.IndexOf(':'); if (separator == -1) { throw new Exception("invalid http header line: " + line); } String name = line.Substring(0, separator); int pos = separator + 1; while ((pos < line.Length) && (line[pos] == ' ')) { pos++; // strip any spaces } string value = line.Substring(pos, line.Length - pos); Console.WriteLine("header: {0}:{1}", name, value); httpHeaders[name] = value; } } private void HandleGetRequest() { srv.HandleGetRequest(this); } private const int BUF_SIZE = 4096; private void HandlePostRequest() { // this post data processing just reads everything into a memory stream. // this is fine for smallish things, but for large stuff we should really // hand an input stream to the request processor. However, the input stream // we hand him needs to let him see the "end of the stream" at this content // length, because otherwise he won't know when he's seen it all! Console.WriteLine("get post data start"); int content_len = 0; using (var ms = new MemoryStream()) { if (httpHeaders.ContainsKey("Content-Length")) { content_len = Convert.ToInt32(httpHeaders["Content-Length"]); if (content_len > MAX_POST_SIZE) { throw new Exception( String.Format("POST Content-Length({0}) too big for this simple server", content_len)); } byte[] buf = new byte[BUF_SIZE]; int to_read = content_len; while (to_read > 0) { Console.WriteLine("starting Read, to_read={0}", to_read); int numread = inputStream.Read(buf, 0, Math.Min(BUF_SIZE, to_read)); Console.WriteLine("read finished, numread={0}", numread); if (numread == 0) { if (to_read == 0) { break; } else { throw new Exception("client disconnected during post"); } } to_read -= numread; ms.Write(buf, 0, numread); } ms.Seek(0, SeekOrigin.Begin); } Console.WriteLine("get post data end"); srv.HandlePostRequest(this, new StreamReader(ms)); } } public void WriteResponse(byte[] content,string contentType) { if (content == null) { WriteError("data not found"); return; } if (string.IsNullOrEmpty(contentType)) { contentType = "text/plain; charset=utf-8"; } WriteResponseHeaders(new[] { "HTTP/1.0 200 OK", "Content-Type: " + contentType, "Content-Length: " + content.Length, "Connection: close" }); using (var byteStream = new BufferedStream(socket.GetStream())) { byteStream.Write(content, 0, content.Length); byteStream.Flush(); } } public void WriteResponse(string content, string contentType) { if (string.IsNullOrEmpty(contentType)) { contentType = "text/plain; charset=utf-8"; } WriteResponseHeaders(new[] { "HTTP/1.0 200 OK", "Content-Type: " + contentType, "Connection: close" }); if (content != null) { outputStream.WriteLine(content); outputStream.Flush(); } } public void WriteResponse(string content) { WriteResponse(content, null); } public void WriteFailure() { WriteResponseHeaders(new[] { "HTTP/1.0 404 File not found", "Connection: close" }); } public void WriteError(string message) { WriteResponse(message); } private void WriteResponseHeaders(string[] headers) { if (headers == null || headers.Length == 0) { throw new Exception("response headers can not be empty"); } foreach (var header in headers) { outputStream.WriteLine(header); } outputStream.WriteLine(""); outputStream.Flush(); } public void WriteResponse(string[] headers, string content) { WriteResponseHeaders(headers); if (content != null) { outputStream.WriteLine(content); outputStream.Flush(); } } public void WriteResponse(string[] headers, byte[] content) { WriteResponseHeaders(headers); if (content != null) { using (var byteStream = new BufferedStream(socket.GetStream())) { byteStream.Write(content, 0, content.Length); byteStream.Flush(); } } } } }