tesses.webserver/Tesses.WebServer.NetStandard/ServerContext.cs

304 lines
8.7 KiB
C#

using System.Collections.Generic;
using System.IO;
using System;
using System.Net;
using System.Text;
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
{
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();
}
/// <summary>
/// Some user data
/// </summary>
public object Tag {get;set;}
/// <summary>
/// Method (ex GET, POST, HEAD)
/// </summary>
public string Method { get; set; }
Func<bool> isConnected;
public bool Connected {
get{
if(isConnected != null)
{
return isConnected();
}
return true;
}
}
public ServerContext(string method,Stream strm,string path,Dictionary<string,List<string>> headers,Func<bool> isConnected)
{
Method = method;
NetworkStream = strm;
RequestHeaders = headers;
ResponseHeaders = new Dictionary<string, List<string>>();
QueryParams = new Dictionary<string, List<string>>();
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<string,List<string>> headers)
{
Method = method;
NetworkStream = strm;
RequestHeaders = headers;
ResponseHeaders = new Dictionary<string, List<string>>();
QueryParams = new Dictionary<string, List<string>>();
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;
}
/// <summary>
/// Reset query parms (If api sets them)
/// </summary>
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;
/// <summary>
/// the /somepath/file?s=42&joel=file relative to Mount
/// </summary>
public string UrlAndQuery {get {
if(!string.IsNullOrWhiteSpace(q_parm))
{
return UrlPath + "?" + q_parm;
}
return UrlPath;
}}
/// <summary>
/// Original Url Path
/// </summary>
/// <value></value>
public string OriginalUrlPath {get; private set;}
/// <summary>
/// Original Url path (includes query)
/// </summary>
/// <value></value>
public string RawUrl {get;private set;}
/// <summary>
/// Query parms string only
/// </summary>
public string QueryParamsString {get {return q_parm;}}
/// <summary>
/// Server ip
/// </summary>
public IPEndPoint Server { get; set; }
/// <summary>
/// Client ip
/// </summary>
public IPEndPoint Client { get; set; }
/// <summary>
/// Host name
/// </summary>
/// <returns></returns>
public string Host { get { return get_host(); } }
/// <summary>
/// Url path (can be eet by Moutable)
/// </summary>
public string UrlPath { get; set; }
/// <summary>
/// Query Params
/// </summary>
public Dictionary<string,List<string>> QueryParams { get; set; }
/// <summary>
/// Request headers
/// </summary>
public Dictionary<string,List<string>> RequestHeaders { get; set; }
/// <summary>
/// Response headers
/// </summary>
public Dictionary<string,List<string>> ResponseHeaders { get; set; }
/// <summary>
/// TCP Stream for http server
/// </summary>
public Stream NetworkStream { get; set; }
/// <summary>
/// Status code for resource
/// </summary>
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;
}
}
}