123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- 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<string, string> httpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- /// <summary>
- /// 显式参数列表
- /// </summary>
- public Dictionary<string, string> GETqueryParams()
- {
- var rslt = new Dictionary<string, string>();
- 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;
- }
- /// <summary>
- /// 显式参数列表
- /// </summary>
- public Dictionary<string, string> POSTqueryParams(StreamReader inputData)
- {
- var rslt = new Dictionary<string, string>();
- 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();
- }
- }
- }
- }
- }
|