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