Rework HTTPRequest connection handling, use HTTP.Parse(..), obsolete HTTPReader

master
Harald Wolff 2020-02-04 21:40:39 +01:00
parent 2542f9a5e1
commit eda733fcbd
3 changed files with 343 additions and 282 deletions

View File

@ -10,304 +10,311 @@ using ln.http.message.parser;
namespace ln.http namespace ln.http
{ {
public class HttpReader //public class HttpReader
{ //{
delegate bool ReadCondition(int b); // delegate bool ReadCondition(int b);
public Stream Stream { get; } // public Stream Stream { get; }
private byte[] buffer = new byte[8192]; // private byte[] buffer = new byte[8192];
private int hlen; // private int hlen;
private int blen; // private int blen;
private int bptr; // private int bptr;
public Endpoint RemoteEndpoint { get; private set; } // public Endpoint RemoteEndpoint { get; private set; }
public HeaderContainer Headers { get; private set; } // public HeaderContainer Headers { get; private set; }
public String Method { get; private set; } // public String Method { get; private set; }
public String URL { get; private set; } // public String URL { get; private set; }
public String Protocol { get; private set; } // public String Protocol { get; private set; }
//public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(); // //public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>();
public bool Valid { get; private set; } = false; // public bool Valid { get; private set; } = false;
public HttpReader(Stream stream) // public HttpReader(Stream stream)
{ // {
Stream = stream; // Stream = stream;
} // }
public HttpReader(Stream stream,Endpoint remoteEndpoint) // public HttpReader(Stream stream,Endpoint remoteEndpoint)
{ // {
Stream = stream; // Stream = stream;
RemoteEndpoint = remoteEndpoint; // RemoteEndpoint = remoteEndpoint;
} // }
public void Read() // public void Read()
{ // {
UnbufferedStreamReader reader = new UnbufferedStreamReader(Stream); // UnbufferedStreamReader reader = new UnbufferedStreamReader(Stream);
string requestLine = reader.ReadLine(); // string requestLine = reader.ReadLine();
string[] requestTokens = requestLine.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); // string[] requestTokens = requestLine.Split(new char[0], StringSplitOptions.RemoveEmptyEntries);
if (requestTokens.Length != 3) // if (requestTokens.Length != 3)
throw new FormatException("request line malformed"); // throw new FormatException("request line malformed");
Method = requestTokens[0]; // Method = requestTokens[0];
URL = requestTokens[1]; // URL = requestTokens[1];
Protocol = requestTokens[2]; // Protocol = requestTokens[2];
Headers = HTTP.ReadHeader(reader); // Headers = HTTP.ReadHeader(reader);
Valid = true; // Valid = true;
} // }
public int ReadByte() // public int ReadByte()
{ // {
if (bptr >= blen) // if (bptr >= blen)
return -1; // return -1;
return buffer[bptr++]; // return buffer[bptr++];
} // }
public int Current // public int Current
{ // {
get // get
{ // {
if (bptr >= blen) // if (bptr >= blen)
return -1; // return -1;
return buffer[bptr]; // return buffer[bptr];
} // }
} // }
public void Reverse() // public void Reverse()
{ // {
if ((bptr > 0) && (bptr < blen)) // if ((bptr > 0) && (bptr < blen))
bptr--; // bptr--;
} // }
public void ReadRequestHead() // public void ReadRequestHead()
{ // {
bptr = 0; // bptr = 0;
do // do
{ // {
int rlen = Stream.Read(buffer, blen, buffer.Length - blen); // int rlen = Stream.Read(buffer, blen, buffer.Length - blen);
if (rlen == 0) // if (rlen == 0)
throw new IOException(); // throw new IOException();
blen += rlen; // blen += rlen;
while (bptr <= (blen - 4)) // while (bptr <= (blen - 4))
{ // {
if ( // if (
(buffer[bptr + 0] == '\r') && // (buffer[bptr + 0] == '\r') &&
(buffer[bptr + 1] == '\n') && // (buffer[bptr + 1] == '\n') &&
(buffer[bptr + 2] == '\r') && // (buffer[bptr + 2] == '\r') &&
(buffer[bptr + 3] == '\n') // (buffer[bptr + 3] == '\n')
) // )
{ // {
hlen = bptr; // hlen = bptr;
bptr = 0; // bptr = 0;
return; // return;
} // }
bptr++; // bptr++;
} // }
byte[] nbuffer = new byte[buffer.Length << 1]; // byte[] nbuffer = new byte[buffer.Length << 1];
Array.Copy(buffer, nbuffer, buffer.Length); // Array.Copy(buffer, nbuffer, buffer.Length);
buffer = nbuffer; // buffer = nbuffer;
} while (blen >= buffer.Length); // } while (blen >= buffer.Length);
bptr = 0; // bptr = 0;
} // }
private String ReadConditional(ReadCondition readCondition,bool reverse = true) // private String ReadConditional(ReadCondition readCondition,bool reverse = true)
{ // {
StringBuilder stringBuilder = new StringBuilder(); // StringBuilder stringBuilder = new StringBuilder();
int b = ReadByte(); // int b = ReadByte();
while ((b != -1) && (readCondition(b)) ) // while ((b != -1) && (readCondition(b)) )
{ // {
stringBuilder.Append((char)b); // stringBuilder.Append((char)b);
b = ReadByte(); // b = ReadByte();
} // }
if (reverse) // if (reverse)
Reverse(); // Reverse();
return stringBuilder.ToString(); // return stringBuilder.ToString();
} // }
public void SkipWhiteSpace() // public void SkipWhiteSpace()
{ // {
while (Char.IsWhiteSpace((char)ReadByte())) { }; // while (Char.IsWhiteSpace((char)ReadByte())) { };
Reverse(); // Reverse();
} // }
public String ReadToken() // public String ReadToken()
{ // {
return ReadConditional((b) => !Char.IsWhiteSpace((char)b)); // return ReadConditional((b) => !Char.IsWhiteSpace((char)b));
} // }
public String ReadLine() // public String ReadLine()
{ // {
int p = bptr; // int p = bptr;
while (p < blen - 1) // while (p < blen - 1)
{ // {
if ((buffer[p] == '\r') && (buffer[p + 1] == '\n')) // if ((buffer[p] == '\r') && (buffer[p + 1] == '\n'))
{ // {
break; // break;
} // }
p++; // p++;
} // }
string result = Encoding.ASCII.GetString(buffer, bptr, p - bptr); // string result = Encoding.ASCII.GetString(buffer, bptr, p - bptr);
bptr = p + 2; // bptr = p + 2;
return result; // return result;
} // }
public String ReadHeaderName() // public String ReadHeaderName()
{ // {
return ReadConditional((b) => b != ':', false).ToUpper(); // return ReadConditional((b) => b != ':', false).ToUpper();
} // }
public String ReadHeaderValue() // public String ReadHeaderValue()
{ // {
String value = ReadLine(); // String value = ReadLine();
while (Char.IsWhiteSpace((char)Current)) // while (Char.IsWhiteSpace((char)Current))
{ // {
value = value + ReadLine(); // value = value + ReadLine();
} // }
return value.Trim(); // return value.Trim();
} // }
//public void ReadHeaders() // //public void ReadHeaders()
//{ // //{
// while (bptr < hlen) // // while (bptr < hlen)
// { // // {
// String name = ReadHeaderName(); // // String name = ReadHeaderName();
// String value = ReadHeaderValue(); // // String value = ReadHeaderValue();
// Headers.Add(name, value); // // Headers.Add(name, value);
// } // // }
//} // //}
public int ReadRequestBody(byte[] dst,int offset,int length) // public int ReadRequestBody(byte[] dst,int offset,int length)
{ // {
int nRead = 0; // //int nRead = 0;
if (bptr < blen) // //if (bptr < blen)
{ // //{
int len = Math.Min(length, blen - bptr); // // int len = Math.Min(length, blen - bptr);
Array.Copy(buffer, bptr, dst, offset, len); // // Array.Copy(buffer, bptr, dst, offset, len);
bptr += len; // // bptr += len;
length -= len; // // length -= len;
offset += len; // // offset += len;
nRead += len; // // nRead += len;
} // //}
if (length > 0) // int nRead = 0;
{ // while (length > 0)
nRead += Stream.Read(dst, offset, length); // {
} // int nr = Stream.Read(dst, offset, length);
return nRead; // if (nr > 0)
} // {
// nRead += nr;
// length -= nr;
// offset += nr;
// }
// }
// return nRead;
// }
/* // /*
byte[] buffer; // byte[] buffer;
int blen = 0; // int blen = 0;
int bptr = 0; // int bptr = 0;
public HttpReader(Stream stream) // public HttpReader(Stream stream)
{ // {
Stream = stream; // Stream = stream;
buffer = new byte[1024]; // buffer = new byte[1024];
} // }
public HttpReader(Stream stream, int buffersize) // public HttpReader(Stream stream, int buffersize)
{ // {
Stream = stream; // Stream = stream;
buffer = new byte[buffersize]; // buffer = new byte[buffersize];
} // }
private int read() // private int read()
{ // {
if (bptr >= blen) // if (bptr >= blen)
{ // {
bptr = 0; // bptr = 0;
blen = Stream.Read(buffer, 0, buffer.Length); // blen = Stream.Read(buffer, 0, buffer.Length);
if (blen <= 0) // if (blen <= 0)
throw new EndOfStreamException(); // throw new EndOfStreamException();
} // }
return buffer[bptr++]; // return buffer[bptr++];
} // }
private int ReadTo(byte[] b,int offset,byte[] mark) // private int ReadTo(byte[] b,int offset,byte[] mark)
{ // {
int pm = 0; // int pm = 0;
int p = offset; // int p = offset;
while (p < b.Length) // while (p < b.Length)
{ // {
b[p] = (byte)read(); // b[p] = (byte)read();
if (b[p] == mark[pm]) // if (b[p] == mark[pm])
{ // {
pm++; // pm++;
if (pm >= mark.Length) // if (pm >= mark.Length)
{ // {
p++; // p++;
break; // break;
} // }
} // }
else // else
{ // {
pm = 0; // pm = 0;
} // }
p++; // p++;
} // }
return p; // return p;
} // }
public String ReadRequestLine(int maxSize = 1024) // public String ReadRequestLine(int maxSize = 1024)
{ // {
byte[] b = new byte[maxSize]; // byte[] b = new byte[maxSize];
int l = ReadTo(b, 0, new byte[] { 0x0d, 0x0a }); // int l = ReadTo(b, 0, new byte[] { 0x0d, 0x0a });
return Encoding.ASCII.GetString(b, 0, l); // return Encoding.ASCII.GetString(b, 0, l);
} // }
public Dictionary<string,string> ReadHTTPHeaders() // public Dictionary<string,string> ReadHTTPHeaders()
{ // {
byte[] b = new byte[8192]; // byte[] b = new byte[8192];
int hlen = ReadTo(b, 0, new byte[] { 0x0d, 0x0a, 0x0d, 0x0a }); // int hlen = ReadTo(b, 0, new byte[] { 0x0d, 0x0a, 0x0d, 0x0a });
Dictionary<string, string> headers = new Dictionary<string, string>(); // Dictionary<string, string> headers = new Dictionary<string, string>();
string rawHeaders = Encoding.ASCII.GetString(b, 0, hlen); // string rawHeaders = Encoding.ASCII.GetString(b, 0, hlen);
String[] rawLines = rawHeaders.Split(new String[] { "\r\n" }, StringSplitOptions.None); // String[] rawLines = rawHeaders.Split(new String[] { "\r\n" }, StringSplitOptions.None);
for (int n = rawLines.Length-1; n >= 0 ; n--) // for (int n = rawLines.Length-1; n >= 0 ; n--)
{ // {
if ((rawLines[n].Length > 0) && (Char.IsWhiteSpace(rawLines[n][0]))) // if ((rawLines[n].Length > 0) && (Char.IsWhiteSpace(rawLines[n][0])))
{ // {
rawLines[n - 1] = rawLines[n - 1] + rawLines[n]; // rawLines[n - 1] = rawLines[n - 1] + rawLines[n];
rawLines[n] = null; // rawLines[n] = null;
} // }
} // }
foreach (String rawLine in rawLines) // foreach (String rawLine in rawLines)
{ // {
if (rawLine != null) // if (rawLine != null)
{ // {
int colon = rawLine.IndexOf(':'); // int colon = rawLine.IndexOf(':');
if (colon > 0) // if (colon > 0)
{ // {
String name = rawLine.Substring(0, colon).Trim().ToUpper(); // String name = rawLine.Substring(0, colon).Trim().ToUpper();
String value = rawLine.Substring(colon + 1).Trim(); // String value = rawLine.Substring(colon + 1).Trim();
headers.Add(name, value); // headers.Add(name, value);
} // }
} // }
} // }
return headers; // return headers;
} // }
public byte[] ReadRequestBody(byte[] buffer) // public byte[] ReadRequestBody(byte[] buffer)
{ // {
return null; // return null;
} // }
*/ // */
} //}
} }

View File

@ -7,6 +7,8 @@ using ln.types.net;
using System.Threading; using System.Threading;
using ln.http.session; using ln.http.session;
using ln.http.message; using ln.http.message;
using ln.http.io;
using ln.http.message.parser;
namespace ln.http namespace ln.http
{ {
@ -42,7 +44,16 @@ namespace ln.http
public HeaderContainer RequestHeaders => requestHeaders; public HeaderContainer RequestHeaders => requestHeaders;
public MemoryStream ContentStream { get; } MemoryStream contentStream;
public MemoryStream ContentStream
{
get
{
if (contentStream == null)
ReadRequestBody();
return contentStream;
}
}
public TextReader ContentReader public TextReader ContentReader
{ {
get get
@ -53,45 +64,89 @@ namespace ln.http
} }
} }
public Stream GetConnectionStream() int requestBodyLength;
{
return connectionStream;
}
StreamReader contentReader;
byte[] requestBody; byte[] requestBody;
Stream connectionStream; StreamReader contentReader;
public HttpRequest(HTTPServer httpServer, HttpReader httpReader, Endpoint localEndpoint) Stream connectionStream;
UnbufferedStreamReader connectionReader;
public Stream GetConnectionStream() => connectionStream;
public HttpRequest(HTTPServer httpServer, Stream clientStream, Endpoint localEndpoint, Endpoint remoteEndpoint)
{ {
HTTPServer = httpServer; HTTPServer = httpServer;
connectionStream = httpReader.Stream; connectionStream = clientStream;
connectionReader = new UnbufferedStreamReader(connectionStream);
LocalEndpoint = localEndpoint; LocalEndpoint = localEndpoint;
RemoteEndpoint = httpReader.RemoteEndpoint; RemoteEndpoint = remoteEndpoint;
Method = httpReader.Method;
Protocol = httpReader.Protocol;
RequestURL = httpReader.URL;
//requestHeaders = new Dictionary<string, string>(httpReader.Headers); ReadRequestLine();
requestHeaders = httpReader.Headers;
requestHeaders = HTTP.ReadHeader(connectionReader);
requestCookies = new Dictionary<string, string>(); requestCookies = new Dictionary<string, string>();
requestParameters = new Dictionary<string, string>(); requestParameters = new Dictionary<string, string>();
Setup(); Setup();
requestBodyLength = int.Parse(GetRequestHeader("content-length", "0"));
}
int clength = int.Parse(GetRequestHeader("content-length", "0")); void ReadRequestLine()
requestBody = new byte[clength]; {
string requestLine = connectionReader.ReadLine();
string[] requestTokens = requestLine.Split(new char[0], StringSplitOptions.RemoveEmptyEntries);
if (clength > 0) if (requestTokens.Length != 3)
throw new BadRequestException();
Method = requestTokens[0];
RequestURL = requestTokens[1];
Protocol = requestTokens[2];
}
public void ReadRequestBody()
{
requestBody = new byte[requestBodyLength];
if (requestBodyLength > 0)
{ {
int nread = httpReader.ReadRequestBody(requestBody, 0, clength); int nRead = 0;
if (nread != clength) int length = requestBodyLength;
throw new HttpException(500, "failed to read request content");
while (length > 0)
{
int nr = connectionStream.Read(requestBody, nRead, length);
if (nr > 0)
{
nRead += nr;
length -= nr;
}
}
} }
ContentStream = new MemoryStream(requestBody); contentStream = new MemoryStream(requestBody);
}
public void FinishRequest()
{
if ((requestBodyLength > 0) && (requestBody == null))
{
int nRead = 0;
int length = requestBodyLength;
byte[] discard = new byte[8192];
while (length > 0)
{
int nr = connectionStream.Read(discard,0,length > discard.Length ? discard.Length : length);
if (nr > 0)
{
nRead += nr;
length -= nr;
}
}
}
} }
public void MakeCurrent() => current.Value = this; public void MakeCurrent() => current.Value = this;

View File

@ -33,10 +33,7 @@ namespace ln.http.connections
{ {
try try
{ {
HttpReader httpReader = new HttpReader(GetStream()); return new HttpRequest(httpServer, GetStream(), Listener.LocalEndpoint, new Endpoint(RemoteHost, RemotePort));
httpReader.Read();
return new HttpRequest(httpServer, httpReader, Listener.LocalEndpoint);
} catch (IOException) } catch (IOException)
{ {
return null; return null;
@ -58,6 +55,8 @@ namespace ln.http.connections
public static void SendResponse(Stream stream, HttpResponse response) public static void SendResponse(Stream stream, HttpResponse response)
{ {
response.HttpRequest.FinishRequest();
response.SetHeader("Content-Length", response.ContentStream.Length.ToString()); response.SetHeader("Content-Length", response.ContentStream.Length.ToString());
StreamWriter streamWriter = new StreamWriter(stream); StreamWriter streamWriter = new StreamWriter(stream);