static public async Task HttpHandleAsync(Stream input, Stream output, Func <HttpRequest, Task <HttpResponse> > handler, CancellationToken cancel = default(CancellationToken)) { //await Task.Delay(TimeSpan.FromMilliseconds(5000)); //Console.WriteLine(RuntimeHelpers.GetHashCode(client)); using (var reader = new BufferedStreamReader(input)) { var httpRequest = await reader.ReadLineAsync(8192); //Console.WriteLine($"httpRequest: {httpRequest}"); var headers = new HttpHeaders(); var headersChars = 0; while (true) { var rawLine = await reader.ReadLineAsync(8192); headersChars += rawLine.Length; if (headersChars > 8192) { throw new IndexOutOfRangeException("Request headers content is too big"); } var line = rawLine.Trim(); if (string.IsNullOrEmpty(line)) { break; } var parts = line.Split(':', 2); headers.Add(parts[0].Trim(), (parts.Length >= 2) ? parts[1].Trim() : ""); } long contentLength; long.TryParse(headers.GetFirst("Content-Length") ?? "0", out contentLength); var limited = reader.Limit(contentLength); var request = new HttpRequest(httpRequest, headers, limited); HttpResponse response; try { response = await handler(request); } catch (Exception e) { response = new HttpResponse(e.Message, HttpStatus.InternalServerError); } await limited.SkipRemaining(); //Console.WriteLine(headers); //Console.WriteLine("Host:" + headers.GetFirst("host")); var sb = new StringBuilder(); sb.Append($"HTTP/1.1 {response.Status.Code} {response.Status.Message}\r\n"); response.Headers.Replace("Content-Length", response.Body.Length.ToString()); response.Headers.Replace("Connection", "Closed"); foreach (var pair in response.Headers.Items) { sb.Append($"{pair.Key}: {pair.Value}\r\n"); } sb.Append("\r\n"); await output.WriteAsync(new ReadOnlyMemory <byte>(Encoding.UTF8.GetBytes(sb.ToString())), cancel); await response.Body.WriteAsync(output); } }
ReadLineAsync(this BufferedStreamReader stream, int maxLength = int.MaxValue) => Encoding.UTF8.GetString(await stream.ReadBytesUntil((byte)'\n', maxLength));