Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //Program.cs
- using System.Buffers;
- using System.Net;
- using System.Net.Sockets;
- using KekLol;
- // папочка, в которой надо смотреть файлики
- const string _wwwRoot = "www";
- // откуда сервер будет слушать подключения
- var _listenEndPoint = IPEndPoint.Parse("0.0.0.0:8080");
- var _serverSocket = new Socket(_listenEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
- var memPool = ArrayPool<byte>.Shared;
- // биндим сокет
- _serverSocket.Bind(_listenEndPoint);
- // начинаем слушать
- _serverSocket.Listen();
- Console.WriteLine($"Сервер на {_listenEndPoint}");
- // бесконечный цикл в котором принимаем соединения
- while (true)
- {
- // ждем подключения
- var client = await _serverSocket.AcceptAsync();
- ProcessClient(client);
- }
- async Task ProcessClient(Socket client)
- {
- using var session = new HttpSession(client, _wwwRoot);
- try{
- await session.Process();
- }catch(Exception ex){}
- }
- //HTTPSession.cs
- using System.Net.Sockets;
- using System.Buffers;
- using System.Text;
- namespace KekLol;
- public sealed class HttpSession : IDisposable
- {
- private Socket _socket;
- private string _rootDirectory;
- //шорткат для того чтобы не писать каждый раз
- private static readonly ArrayPool<byte> _arrayPool = ArrayPool<byte>.Shared;
- public HttpSession(Socket socket, string rootDirectory)
- {
- _socket = socket;
- _rootDirectory = rootDirectory;
- }
- public async Task Process()
- {
- try
- {
- var buffer = _arrayPool.Rent(1024);
- HttpRequest request = new();
- try
- {
- while (true)
- {
- //число байтиков что получили в запросе
- var recBytes = await _socket.ReceiveAsync(buffer, SocketFlags.None);
- //ну, собственно, клиент пидорас и отключился(())
- if (recBytes == 0)
- {
- break;
- }
- //аппендим то что дошло
- await request.Append(buffer[..recBytes]);
- //если не удалось распарсить запрос - считаем, что не все еще получили, а потому - идем на след заход
- var requestInfo = await request.ParseBuffer();
- if(requestInfo is null){
- continue;
- }
- var responce = await GetResponce(requestInfo);
- await _socket.SendAsync(responce);
- break;
- }
- }
- finally
- {
- _arrayPool.Return(buffer);
- }
- }
- //нам наплевать че там не так
- catch (Exception ex) { }
- }
- private async Task<byte[]> GetResponce(HttpRequestInfo request)
- {
- HttpResponse responce = new();
- responce.UseHeader("Server", "KekLol");
- string filePath = null;
- if(request.Path=="/"){
- filePath = $"{_rootDirectory}/index.html";
- }
- else {
- filePath = $"{_rootDirectory}{request.Path}";
- }
- if(File.Exists(filePath)){
- FileInfo fi = new FileInfo(filePath);
- var size = fi.Length;
- responce.UseHeader("Content-Type", "text/html")
- .UseHeader("Content-Length", $"{size}")
- .UseStatusCode(200)
- .UseReason("OK")
- .UseBody(await File.ReadAllTextAsync(filePath));
- }
- else {
- responce.UseStatusCode(404)
- .UseReason("Not found");
- }
- return Encoding.ASCII.GetBytes(responce.GetString());
- }
- public void Dispose()
- {
- _socket.Dispose();
- }
- }
- //HTTPRequest.cs
- using System.Buffers;
- using System.IO.Pipelines;
- using System.Text;
- namespace KekLol;
- public sealed class HttpRequest
- {
- static readonly byte[] _stringSeparator = new[] { (byte)'\r', (byte)'\n' };
- Pipe pipe = new();
- public async Task Append(ReadOnlyMemory<byte> data)
- {
- await pipe.Writer.WriteAsync(data);
- }
- public async Task<HttpRequestInfo?> ParseBuffer()
- {
- var readResult = await pipe.Reader.ReadAsync();
- var buffer = readResult.Buffer;
- try
- {
- return ProcessBuffer_internal(buffer);
- }
- finally
- {
- pipe.Reader.AdvanceTo(buffer.End, buffer.End);
- }
- }
- private bool _firstLineParsed = false;
- private bool _headersParsed = false;
- private HttpRequestInfo? ProcessBuffer_internal(ReadOnlySequence<byte> buffer)
- {
- var reader = new SequenceReader<byte>(buffer);
- if(ParseRequestLineLine(reader, out var requestLine))
- {
- var parts = requestLine.Split(' ', StringSplitOptions.RemoveEmptyEntries);
- var method = parts[0];
- var path = parts[1];
- var protocol = parts[2];
- byte[] body = Array.Empty<byte>();
- if(ParseHeaders(reader, out var headers))
- {
- //смотрим надо ли тело читать...
- return new HttpRequestInfo {
- Path = path,
- Method = method,
- ProtocolVersion = protocol,
- Headers = headers,
- Body = body
- };
- }
- }
- return null;
- }
- private bool ParseRequestLineLine(in SequenceReader<byte> reader, out string line)
- {
- line = null;
- if(reader.TryReadTo(out ReadOnlySpan<byte> lineSequnece,_stringSeparator ,true)){
- line = Encoding.ASCII.GetString(lineSequnece);
- return true;
- }
- return false;
- }
- private bool ParseHeaders(in SequenceReader<byte> reader, out Dictionary<string,string> headers)
- {
- //TODO)))
- headers = new();
- return true;
- }
- }
- //HttpRequestInfo.cs
- namespace KekLol;
- public sealed class HttpRequestInfo
- {
- public string Path { get; set; }
- public string Method { get; set; }
- public string ProtocolVersion { get; set; }
- public Dictionary<string, string> Headers { get; set; }
- public byte[] Body { get; set; }
- }
- //HttpResponse.cs
- using System.Text;
- namespace KekLol;
- public sealed class HttpResponse
- {
- private int _statusCode;
- private Dictionary<string, string> _headers = new();
- private string _reason;
- private string _body;
- public HttpResponse() { }
- public HttpResponse UseStatusCode(int statusCode)
- {
- _statusCode = statusCode;
- return this;
- }
- public HttpResponse UseReason(string reason)
- {
- _reason = reason;
- return this;
- }
- public HttpResponse UseBody(string body)
- {
- _body = body;
- return this;
- }
- public HttpResponse UseHeader(string headerName, string headerValue)
- {
- _headers[headerName] = headerValue;
- return this;
- }
- public string GetString()
- {
- StringBuilder sb = new();
- sb.AppendLine($"HTTP/1.1 {_statusCode} {_reason}");
- sb.AppendLine( string.Join("\r\n", _headers.Select(x => $"{_headers.Keys}: {_headers.Values}")));
- sb.AppendLine("\r\n");
- sb.AppendLine(_body);
- return sb.ToString();
- }
- }
Add Comment
Please, Sign In to add comment