Guest User

Untitled

a guest
Jan 29th, 2023
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 7.75 KB | Source Code | 0 0
  1. //Program.cs
  2. using System.Buffers;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using KekLol;
  6.  
  7. // папочка, в которой надо смотреть файлики
  8. const string _wwwRoot = "www";
  9. // откуда сервер будет слушать подключения
  10. var _listenEndPoint = IPEndPoint.Parse("0.0.0.0:8080");
  11.  
  12. var _serverSocket = new Socket(_listenEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  13.  
  14. var memPool = ArrayPool<byte>.Shared;
  15.  
  16. // биндим сокет
  17. _serverSocket.Bind(_listenEndPoint);
  18.  
  19. // начинаем слушать
  20. _serverSocket.Listen();
  21. Console.WriteLine($"Сервер на {_listenEndPoint}");
  22.  
  23. // бесконечный цикл в котором принимаем соединения
  24. while (true)
  25. {
  26.     // ждем подключения
  27.     var client = await _serverSocket.AcceptAsync();
  28.     ProcessClient(client);
  29. }
  30.  
  31.  
  32. async Task ProcessClient(Socket client)
  33. {
  34.     using var session = new HttpSession(client, _wwwRoot);
  35.     try{
  36.         await session.Process();
  37.     }catch(Exception ex){}
  38. }
  39.  
  40. //HTTPSession.cs
  41. using System.Net.Sockets;
  42. using System.Buffers;
  43. using System.Text;
  44. namespace KekLol;
  45. public sealed class HttpSession : IDisposable
  46. {
  47.  
  48.     private Socket _socket;
  49.     private string _rootDirectory;
  50.     //шорткат для того чтобы не писать каждый раз
  51.     private static readonly ArrayPool<byte> _arrayPool = ArrayPool<byte>.Shared;
  52.  
  53.     public HttpSession(Socket socket, string rootDirectory)
  54.     {
  55.         _socket = socket;
  56.         _rootDirectory = rootDirectory;
  57.     }
  58.  
  59.     public async Task Process()
  60.     {
  61.         try
  62.         {
  63.             var buffer = _arrayPool.Rent(1024);
  64.             HttpRequest request = new();
  65.             try
  66.             {
  67.                 while (true)
  68.                 {
  69.                     //число байтиков что получили в запросе
  70.                     var recBytes = await _socket.ReceiveAsync(buffer, SocketFlags.None);
  71.                     //ну, собственно, клиент пидорас и отключился(())
  72.                     if (recBytes == 0)
  73.                     {
  74.                         break;
  75.                     }
  76.                     //аппендим то что дошло
  77.                     await request.Append(buffer[..recBytes]);
  78.                     //если не удалось распарсить запрос - считаем, что не все еще получили, а потому - идем на след заход
  79.                     var requestInfo = await request.ParseBuffer();
  80.                     if(requestInfo is null){
  81.                         continue;
  82.                     }
  83.                     var responce = await GetResponce(requestInfo);
  84.                     await _socket.SendAsync(responce);
  85.                     break;
  86.                 }
  87.  
  88.             }
  89.             finally
  90.             {
  91.                 _arrayPool.Return(buffer);
  92.             }
  93.  
  94.  
  95.         }
  96.         //нам наплевать че там не так
  97.         catch (Exception ex) { }
  98.     }
  99.  
  100.     private async Task<byte[]> GetResponce(HttpRequestInfo request)
  101.     {
  102.         HttpResponse responce = new();
  103.         responce.UseHeader("Server", "KekLol");
  104.         string filePath = null;
  105.         if(request.Path=="/"){
  106.             filePath = $"{_rootDirectory}/index.html";            
  107.         }
  108.         else {
  109.             filePath = $"{_rootDirectory}{request.Path}";
  110.         }
  111.         if(File.Exists(filePath)){
  112.             FileInfo fi = new FileInfo(filePath);
  113.             var size = fi.Length;            
  114.             responce.UseHeader("Content-Type", "text/html")
  115.                 .UseHeader("Content-Length", $"{size}")
  116.                 .UseStatusCode(200)
  117.                 .UseReason("OK")
  118.                 .UseBody(await File.ReadAllTextAsync(filePath));
  119.         }
  120.         else {
  121.             responce.UseStatusCode(404)
  122.                 .UseReason("Not found");
  123.         }
  124.         return Encoding.ASCII.GetBytes(responce.GetString());
  125.     }
  126.  
  127.     public void Dispose()
  128.     {
  129.         _socket.Dispose();
  130.     }
  131. }
  132.  
  133. //HTTPRequest.cs
  134. using System.Buffers;
  135. using System.IO.Pipelines;
  136. using System.Text;
  137. namespace KekLol;
  138. public sealed class HttpRequest
  139. {
  140.     static readonly byte[] _stringSeparator = new[] { (byte)'\r', (byte)'\n' };
  141.     Pipe pipe = new();
  142.     public async Task Append(ReadOnlyMemory<byte> data)
  143.     {
  144.         await pipe.Writer.WriteAsync(data);
  145.     }
  146.  
  147.     public async Task<HttpRequestInfo?> ParseBuffer()
  148.     {
  149.         var readResult = await pipe.Reader.ReadAsync();
  150.         var buffer = readResult.Buffer;
  151.         try
  152.         {
  153.             return ProcessBuffer_internal(buffer);
  154.         }
  155.         finally
  156.         {
  157.             pipe.Reader.AdvanceTo(buffer.End, buffer.End);
  158.         }
  159.     }
  160.    
  161.     private bool _firstLineParsed = false;
  162.     private bool _headersParsed = false;
  163.     private HttpRequestInfo? ProcessBuffer_internal(ReadOnlySequence<byte> buffer)
  164.     {
  165.         var reader = new SequenceReader<byte>(buffer);
  166.  
  167.         if(ParseRequestLineLine(reader, out var requestLine))
  168.         {
  169.             var parts = requestLine.Split(' ', StringSplitOptions.RemoveEmptyEntries);
  170.             var method = parts[0];
  171.             var path = parts[1];
  172.             var protocol = parts[2];
  173.             byte[] body = Array.Empty<byte>();
  174.             if(ParseHeaders(reader, out var headers))
  175.             {
  176.                 //смотрим надо ли тело читать...
  177.                 return new HttpRequestInfo {
  178.                     Path = path,
  179.                     Method = method,
  180.                     ProtocolVersion = protocol,
  181.                     Headers = headers,
  182.                     Body = body
  183.                 };
  184.             }
  185.         }
  186.         return null;
  187.     }
  188.  
  189.     private bool ParseRequestLineLine(in SequenceReader<byte> reader, out string line)
  190.     {
  191.         line = null;
  192.         if(reader.TryReadTo(out ReadOnlySpan<byte> lineSequnece,_stringSeparator ,true)){
  193.             line = Encoding.ASCII.GetString(lineSequnece);
  194.             return true;
  195.         }
  196.  
  197.         return false;
  198.     }
  199.     private bool ParseHeaders(in SequenceReader<byte> reader, out Dictionary<string,string> headers)
  200.     {
  201.         //TODO)))
  202.         headers = new();
  203.         return true;
  204.     }
  205. }
  206.  
  207. //HttpRequestInfo.cs
  208. namespace KekLol;
  209.  
  210. public sealed class HttpRequestInfo
  211. {
  212.     public string Path { get; set; }
  213.     public string Method { get; set; }
  214.     public string ProtocolVersion { get; set; }
  215.     public Dictionary<string, string> Headers { get; set; }
  216.     public byte[] Body { get; set; }
  217. }
  218.  
  219. //HttpResponse.cs
  220. using System.Text;
  221. namespace KekLol;
  222. public sealed class HttpResponse
  223. {
  224.     private int _statusCode;
  225.     private Dictionary<string, string> _headers = new();
  226.     private string _reason;
  227.     private string _body;
  228.     public HttpResponse() { }
  229.     public HttpResponse UseStatusCode(int statusCode)
  230.     {
  231.         _statusCode = statusCode;
  232.         return this;
  233.     }
  234.     public HttpResponse UseReason(string reason)
  235.     {
  236.         _reason = reason;
  237.         return this;
  238.     }
  239.     public HttpResponse UseBody(string body)
  240.     {
  241.         _body = body;
  242.         return this;
  243.     }
  244.     public HttpResponse UseHeader(string headerName, string headerValue)
  245.     {
  246.         _headers[headerName] = headerValue;
  247.         return this;
  248.     }
  249.     public string GetString()
  250.     {
  251.         StringBuilder sb = new();
  252.         sb.AppendLine($"HTTP/1.1 {_statusCode} {_reason}");
  253.         sb.AppendLine( string.Join("\r\n", _headers.Select(x => $"{_headers.Keys}: {_headers.Values}")));
  254.         sb.AppendLine("\r\n");
  255.         sb.AppendLine(_body);
  256.         return sb.ToString();
  257.     }
  258. }
Add Comment
Please, Sign In to add comment