tesses.http/Tesses.Http/HttpParser.cs

354 lines
9.4 KiB
C#

using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using System.Net.Http;
namespace Tesses.Http
{
public struct FirstLine
{
public FirstLine(string line)
{
LineText=line;
}
public string LineText {get;set;}
public static implicit operator FirstLine(string text)
{
return new FirstLine(text);
}
public static implicit operator string(FirstLine line)
{
return line.ToString();
}
public override string ToString()
{
return LineText;
}
}
public struct RequestLine
{
public RequestLine(string method,string path,string httpVersion)
{
Method = method;
Path= path;
HttpVersion=httpVersion;
}
public RequestLine(string method,string path)
{
Method=method;
Path=path;
HttpVersion = "HTTP/1.1";
}
public string Method {get;set;}
public string Path {get;set;}
public string HttpVersion {get;set;}
public static implicit operator RequestLine(FirstLine line)
{
string[] data=line.LineText.Split(' ');
RequestLine Rline=new RequestLine(data[0],data[1],data[2]);
return Rline;
}
public static implicit operator FirstLine(RequestLine line)
{
return new FirstLine(line.ToString());
}
public static implicit operator RequestLine(string path)
{
RequestLine req=new RequestLine();
req.Method="GET";
req.Path=path;
req.HttpVersion="HTTP/1.1";
return req;
}
public override string ToString()
{
return $"{Method} {Path} {HttpVersion}";
}
}
public struct StatusLine
{
public StatusLine(string httpVersion,HttpStatusCode code)
{
HttpVersion = httpVersion;
StatusCode=(int)code;
}
public StatusLine(string httpVersion,int code)
{
HttpVersion = httpVersion;
StatusCode = code;
}
public string HttpVersion {get;set;}
public int StatusCode {get;set;}
public static implicit operator StatusLine(FirstLine line)
{
//HTTP/1.1 200 OK
StatusLine Sline=new StatusLine();
string[] data=line.LineText.Split(' ');
Sline.HttpVersion = data[0];
int n=200;
if(!int.TryParse(data[1],out n))
{
n=500;
}
Sline.StatusCode=n;
return Sline;
}
public static implicit operator StatusLine(int code)
{
StatusLine line = new StatusLine();
line.HttpVersion= "HTTP/1.1";
line.StatusCode=code;
return line;
}
public static implicit operator FirstLine(StatusLine line)
{
return new FirstLine(line.ToString());
}
public override string ToString()
{
return $"{HttpVersion} {StatusCode}";
}
}
public class HeaderCollection : Dictionary<string,List<string>>
{
public HeaderCollection()
{
FirstLine="";
}
public HeaderCollection(string firstLine)
{
FirstLine=firstLine;
}
public HeaderCollection(Stream strm)
{
string[] headers=strm.ReadHttpHeaders().Split(new string[]{"\r\n"},StringSplitOptions.RemoveEmptyEntries);
foreach(var hdr in headers)
{
Add(hdr);
}
}
public FirstLine FirstLine {get;set;}
public void Add(string header)
{
if(header.Contains(":"))
{
//its a kvp
string[] hdr=header.Split(new string[]{": "},2,StringSplitOptions.None);
if(hdr.Length ==1)
{
this.Add(hdr[0],"");
}else{
this.Add(hdr[0],hdr[1]);
}
}else{
FirstLine = header;
}
}
public override string ToString()
{
StringBuilder b=new StringBuilder();
b.Append($"{FirstLine}\r\n");
foreach(var kvps in this)
{
foreach(var value in kvps.Value)
{
b.Append($"{kvps.Key}: {value}\r\n");
}
}
b.Append("\r\n");
return b.ToString();
}
internal byte[] AsBytes()
{
return Encoding.UTF8.GetBytes(ToString());
}
public void WriteHeaders(Stream strm)
{
byte[] buff=AsBytes();
strm.Write(buff,0,buff.Length);
strm.Flush();
}
}
public class HttpParser
{
private class HttpBodyStream : Stream
{
Stream strm;
long length;
public HttpBodyStream(Stream src,long len)
{
this.strm = src;
this.length=len;
}
public override bool CanRead => strm.CanRead;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => length;
private long read=0;
public override long Position { get {return read;} set => throw new NotImplementedException(); }
public override void Flush()
{
strm.Flush();
}
public override int ReadByte()
{
if(length > 0 && read >= length) return -1;
return strm.ReadByte();
}
public override int Read(byte[] buffer, int offset, int count)
{
int read0=0;
if(length == 0) {
read0= strm.Read(buffer,offset,count);
read+=read0;
return read0;
}
//i want to read some data
read0=(int)Math.Min(count,length-read);
if(read0 == 0) return 0;
read0=strm.Read(buffer,offset,read0);
read+=read0;
return read0;
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
int read0=0;
if(length == 0) {
read0= await strm.ReadAsync(buffer,offset,count);
read+=read0;
return read0;
}
//i want to read some data
read0=(int)Math.Min(count,length-read);
if(read0 == 0) return 0;
read0=await strm.ReadAsync(buffer,offset,read0);
read+=read0;
return read0;
}
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)
{
throw new NotImplementedException();
}
public override void Close()
{
strm.Close();
}
protected override void Dispose(bool disposing)
{
if(disposing)
{
strm.Dispose();
}
}
public override async Task FlushAsync(CancellationToken cancellationToken)
{
await strm.FlushAsync(cancellationToken);
}
}
Stream _strm;
public HttpParser(Stream strm)
{
_strm=strm;
}
public Stream GetRawStream()
{
return _strm;
}
public HeaderCollection SentHeaders {get;set;}
public HeaderCollection ReceivedHeaders {get;set;}
public void SendHeaders()
{
SentHeaders.WriteHeaders(_strm);
}
public void ReceiveHeaders()
{
ReceivedHeaders = new HeaderCollection(_strm);
}
public void WriteBody(Stream strm)
{
strm.CopyTo(_strm);
_strm.Flush();
}
public void WriteBody(string text)
{
WriteBody(System.Text.Encoding.UTF8.GetBytes(text));
}
public void WriteBody(byte[] data)
{
using(var ms = new MemoryStream())
{
ms.Write(data,0,data.Length);
ms.Seek(0,SeekOrigin.Begin);
WriteBody(ms);
}
}
public Stream ReadBody()
{
long res=0;
if(!ReceivedHeaders.TryGetFirst("Content-Length",out res))
{
res=0;
}
return new HttpBodyStream(_strm,res);
}
}
}