2022-04-02 18:59:12 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Net;
|
2024-03-18 12:31:51 +00:00
|
|
|
|
using System.Text;
|
2022-04-02 18:59:12 +00:00
|
|
|
|
|
|
|
|
|
namespace Tesses.WebServer
|
|
|
|
|
{
|
2022-07-27 02:52:32 +00:00
|
|
|
|
|
|
|
|
|
internal class SizedStream : Stream
|
|
|
|
|
{
|
|
|
|
|
Stream strm;
|
|
|
|
|
long len;
|
|
|
|
|
long pos=0;
|
|
|
|
|
public SizedStream(Stream src,long len)
|
|
|
|
|
{
|
|
|
|
|
this.strm=src;
|
|
|
|
|
this.len=len;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-27 23:30:52 +00:00
|
|
|
|
public override int ReadByte()
|
|
|
|
|
{
|
|
|
|
|
if(pos >= len) return -1;
|
|
|
|
|
return strm.ReadByte();
|
|
|
|
|
}
|
2022-07-27 02:52:32 +00:00
|
|
|
|
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);
|
2022-07-27 23:30:52 +00:00
|
|
|
|
if(read == 0) return 0;
|
|
|
|
|
|
2022-07-27 02:52:32 +00:00
|
|
|
|
read=strm.Read(buffer,offset,read);
|
2022-07-27 23:30:52 +00:00
|
|
|
|
|
2022-07-27 02:52:32 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-02 18:59:12 +00:00
|
|
|
|
public class ServerContext
|
|
|
|
|
{
|
2024-03-18 12:31:51 +00:00
|
|
|
|
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();
|
|
|
|
|
}
|
2022-04-24 18:56:06 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Some user data
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
|
|
public object Tag {get;set;}
|
2022-04-23 18:25:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Method (ex GET, POST, HEAD)
|
|
|
|
|
/// </summary>
|
2022-04-02 18:59:12 +00:00
|
|
|
|
public string Method { get; set; }
|
2022-09-01 05:26:26 +00:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2022-04-02 18:59:12 +00:00
|
|
|
|
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>>();
|
2022-04-03 14:39:54 +00:00
|
|
|
|
QueryParams = new Dictionary<string, List<string>>();
|
2022-06-22 20:03:20 +00:00
|
|
|
|
ResponseHeaders.Add("Server","Tesses.WebServer");
|
|
|
|
|
ResponseHeaders.Add("Connection","close");
|
2022-04-23 18:25:45 +00:00
|
|
|
|
RawUrl=path;
|
2022-04-02 18:59:12 +00:00
|
|
|
|
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];
|
2022-04-23 18:25:45 +00:00
|
|
|
|
OriginalUrlPath=splitUrl[0];
|
2022-04-02 18:59:12 +00:00
|
|
|
|
if (splitUrl.Length == 2)
|
|
|
|
|
{
|
|
|
|
|
//local=jim&john_surname=connor&demi_surname=lovato&local=tim
|
|
|
|
|
//we want to split on &
|
2022-04-03 14:39:54 +00:00
|
|
|
|
q_parm = splitUrl[1];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
q_parm = "";
|
|
|
|
|
}
|
|
|
|
|
ResetQueryParms();
|
|
|
|
|
}
|
2022-09-01 05:26:26 +00:00
|
|
|
|
isConnected=null;
|
2022-04-03 14:39:54 +00:00
|
|
|
|
}
|
2022-04-23 18:25:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Reset query parms (If api sets them)
|
|
|
|
|
/// </summary>
|
2022-04-03 14:39:54 +00:00
|
|
|
|
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)
|
2022-04-02 18:59:12 +00:00
|
|
|
|
{
|
2022-06-22 20:03:20 +00:00
|
|
|
|
value = WebUtility.UrlDecode( itemSplit[1]);
|
2022-04-02 18:59:12 +00:00
|
|
|
|
}
|
2022-04-03 14:39:54 +00:00
|
|
|
|
QueryParams.Add(key, value);
|
2022-04-02 18:59:12 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-03 14:39:54 +00:00
|
|
|
|
|
2022-04-02 18:59:12 +00:00
|
|
|
|
}
|
|
|
|
|
private string get_host()
|
|
|
|
|
{
|
|
|
|
|
if(RequestHeaders.ContainsKey("Host"))
|
|
|
|
|
{
|
|
|
|
|
return RequestHeaders.GetFirst("Host");
|
|
|
|
|
}
|
2022-04-03 14:39:54 +00:00
|
|
|
|
return Server.Address.ToString();
|
2022-04-02 18:59:12 +00:00
|
|
|
|
}
|
2022-04-03 14:39:54 +00:00
|
|
|
|
string q_parm;
|
2022-04-23 18:25:45 +00:00
|
|
|
|
/// <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>
|
|
|
|
|
|
2022-04-03 14:39:54 +00:00
|
|
|
|
public IPEndPoint Server { get; set; }
|
2022-04-23 18:25:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Client ip
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
2022-04-03 14:39:54 +00:00
|
|
|
|
public IPEndPoint Client { get; set; }
|
2022-04-23 18:25:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Host name
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2022-04-02 18:59:12 +00:00
|
|
|
|
public string Host { get { return get_host(); } }
|
2022-04-23 18:25:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Url path (can be eet by Moutable)
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
2022-04-02 18:59:12 +00:00
|
|
|
|
public string UrlPath { get; set; }
|
2022-04-23 18:25:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Query Params
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
2022-04-02 18:59:12 +00:00
|
|
|
|
public Dictionary<string,List<string>> QueryParams { get; set; }
|
2022-04-23 18:25:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Request headers
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
2022-04-02 18:59:12 +00:00
|
|
|
|
public Dictionary<string,List<string>> RequestHeaders { get; set; }
|
2022-04-23 18:25:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Response headers
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
2022-04-02 18:59:12 +00:00
|
|
|
|
public Dictionary<string,List<string>> ResponseHeaders { get; set; }
|
2022-04-23 18:25:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// TCP Stream for http server
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
2022-04-02 18:59:12 +00:00
|
|
|
|
public Stream NetworkStream { get; set; }
|
2022-04-23 18:25:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Status code for resource
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
|
|
public int StatusCode { get; set; }
|
2022-07-27 02:52:32 +00:00
|
|
|
|
|
|
|
|
|
public Stream GetRequestStream()
|
|
|
|
|
{
|
|
|
|
|
string len_Str;
|
|
|
|
|
long len;
|
|
|
|
|
if(RequestHeaders.TryGetFirst("Content-Length",out len_Str))
|
|
|
|
|
{
|
|
|
|
|
if(long.TryParse(len_Str,out len))
|
|
|
|
|
{
|
2022-07-27 23:30:52 +00:00
|
|
|
|
//DajuricSimpleHttpExtensions.Print($"Content-Length: {len}");
|
2022-07-27 02:52:32 +00:00
|
|
|
|
return new SizedStream(NetworkStream,len);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-18 12:31:51 +00:00
|
|
|
|
else if(RequestHeaders.TryGetFirst("Transfer-Encoding",out var res) && res == "chunked")
|
|
|
|
|
{
|
|
|
|
|
return new ChunkedStream(NetworkStream,true);
|
|
|
|
|
}
|
2022-07-27 23:30:52 +00:00
|
|
|
|
//DajuricSimpleHttpExtensions.Print("Returns NetworkStream");
|
2022-07-27 02:52:32 +00:00
|
|
|
|
return NetworkStream;
|
|
|
|
|
}
|
2022-04-02 18:59:12 +00:00
|
|
|
|
}
|
2022-04-23 18:25:45 +00:00
|
|
|
|
}
|