using System.Collections.Generic; using System.IO; using System; using System.Net; using System.Text; using System.Threading; namespace Tesses.WebServer { internal class SizedStream : Stream { Stream strm; long len; long pos=0; public SizedStream(Stream src,long len) { this.strm=src; this.len=len; } public override int ReadByte() { if(pos >= len) return -1; return strm.ReadByte(); } public override bool CanRead => strm.CanRead; public override bool CanSeek => false; public override bool CanWrite => strm.CanWrite; public override long Length => len; public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public override void Flush() { strm.Flush(); } public override int Read(byte[] buffer, int offset, int count) { int read=(int)Math.Min(count,len-pos); if(read == 0) return 0; read=strm.Read(buffer,offset,read); pos+=read; return read; } public override long Seek(long offset, SeekOrigin origin) { throw new NotImplementedException(); } public override void SetLength(long value) { throw new NotImplementedException(); } public override void Write(byte[] buffer, int offset, int count) { int read=(int)Math.Min(count,len-pos); pos+=read; strm.Write(buffer,offset,read); } } public class ServerContext { static Mutex mtx=new Mutex(); static long _unique=0; public static long UniqueNumber() { mtx.WaitOne(); long u=_unique++; mtx.ReleaseMutex(); return u; } const string bad_chars = "<>?/\\\"*|:"; public static string FixFileName(string filename,bool requireAscii=false) { StringBuilder builder=new StringBuilder(); foreach(var c in filename) { if(char.IsControl(c)) continue; if(requireAscii && c > 127) continue; if(bad_chars.Contains(c.ToString())) continue; builder.Append(c); } return builder.ToString(); } /// /// Some user data /// public object Tag {get;set;} /// /// Method (ex GET, POST, HEAD) /// public string Method { get; set; } Func isConnected; public bool Connected { get{ if(isConnected != null) { return isConnected(); } return true; } } public ServerContext(string method,Stream strm,string path,Dictionary> headers,Func isConnected) { Method = method; NetworkStream = strm; RequestHeaders = headers; ResponseHeaders = new Dictionary>(); QueryParams = new Dictionary>(); ResponseHeaders.Add("Server","Tesses.WebServer"); ResponseHeaders.Add("Connection","close"); RawUrl=path; StatusCode = 200; // /joel/path/luigi?local=jim&john_surname=connor&demi_surname=lovato&local=tim string[] splitUrl = path.Split(new char[] { '?' }, 2); if (splitUrl.Length > 0) { UrlPath = splitUrl[0]; OriginalUrlPath=splitUrl[0]; if (splitUrl.Length == 2) { //local=jim&john_surname=connor&demi_surname=lovato&local=tim //we want to split on & q_parm = splitUrl[1]; } else { q_parm = ""; } ResetQueryParms(); } this.isConnected=isConnected; } public ServerContext(string method,Stream strm,string path,Dictionary> headers) { Method = method; NetworkStream = strm; RequestHeaders = headers; ResponseHeaders = new Dictionary>(); QueryParams = new Dictionary>(); ResponseHeaders.Add("Server","Tesses.WebServer"); ResponseHeaders.Add("Connection","close"); RawUrl=path; StatusCode = 200; // /joel/path/luigi?local=jim&john_surname=connor&demi_surname=lovato&local=tim string[] splitUrl = path.Split(new char[] { '?' }, 2); if (splitUrl.Length > 0) { UrlPath = splitUrl[0]; OriginalUrlPath=splitUrl[0]; if (splitUrl.Length == 2) { //local=jim&john_surname=connor&demi_surname=lovato&local=tim //we want to split on & q_parm = splitUrl[1]; } else { q_parm = ""; } ResetQueryParms(); } isConnected=null; } /// /// Reset query parms (If api sets them) /// public void ResetQueryParms() { QueryParams.Clear(); foreach (var item in q_parm.Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries)) { //Console.WriteLine(item); var itemSplit = item.Split(new char[] { '=' }, 2); if (itemSplit.Length > 0) { string key = itemSplit[0]; string value = ""; if (itemSplit.Length == 2) { value = WebUtility.UrlDecode( itemSplit[1]); } QueryParams.Add(key, value); } } } private string get_host() { if(RequestHeaders.ContainsKey("Host")) { return RequestHeaders.GetFirst("Host"); } return Server.Address.ToString(); } string q_parm; /// /// the /somepath/file?s=42&joel=file relative to Mount /// public string UrlAndQuery {get { if(!string.IsNullOrWhiteSpace(q_parm)) { return UrlPath + "?" + q_parm; } return UrlPath; }} /// /// Original Url Path /// /// public string OriginalUrlPath {get; private set;} /// /// Original Url path (includes query) /// /// public string RawUrl {get;private set;} /// /// Query parms string only /// public string QueryParamsString {get {return q_parm;}} /// /// Server ip /// public IPEndPoint Server { get; set; } /// /// Client ip /// public IPEndPoint Client { get; set; } /// /// Host name /// /// public string Host { get { return get_host(); } } /// /// Url path (can be eet by Moutable) /// public string UrlPath { get; set; } /// /// Query Params /// public Dictionary> QueryParams { get; set; } /// /// Request headers /// public Dictionary> RequestHeaders { get; set; } /// /// Response headers /// public Dictionary> ResponseHeaders { get; set; } /// /// TCP Stream for http server /// public Stream NetworkStream { get; set; } /// /// Status code for resource /// public int StatusCode { get; set; } public Stream GetRequestStream() { string len_Str; long len; if(RequestHeaders.TryGetFirst("Content-Length",out len_Str)) { if(long.TryParse(len_Str,out len)) { //DajuricSimpleHttpExtensions.Print($"Content-Length: {len}"); return new SizedStream(NetworkStream,len); } } else if(RequestHeaders.TryGetFirst("Transfer-Encoding",out var res) && res == "chunked") { return new ChunkedStream(NetworkStream,true); } //DajuricSimpleHttpExtensions.Print("Returns NetworkStream"); return NetworkStream; } } }