diff --git a/samples/AzCopyCore/AzCopyCore/AzCopyCore.csproj b/samples/AzCopyCore/AzCopyCore/AzCopyCore.csproj index 141dd99c4c2..6567f2197ab 100644 --- a/samples/AzCopyCore/AzCopyCore/AzCopyCore.csproj +++ b/samples/AzCopyCore/AzCopyCore/AzCopyCore.csproj @@ -17,13 +17,13 @@ - - - - - - - + + + + + + + diff --git a/samples/AzCopyCore/AzCopyCore/SocketClient.cs b/samples/AzCopyCore/AzCopyCore/SocketClient.cs index 0883035a290..6b6e3de803f 100644 --- a/samples/AzCopyCore/AzCopyCore/SocketClient.cs +++ b/samples/AzCopyCore/AzCopyCore/SocketClient.cs @@ -86,11 +86,40 @@ public async ValueTask SendRequest(TRequest requ await request.WriteAsync(_requestPipe.Writer).ConfigureAwait(false); var reader = _responsePipe.Reader; - var response = await HttpExtensions.ParseAsync(reader, Log).ConfigureAwait(false); + var response = await ParseAsync(reader, Log).ConfigureAwait(false); response.OnBody(reader); return response; } + static HttpParser s_headersParser = new HttpParser(); + + // TODO (pri 3): Add to the platform, but this would require common logging API + public static async ValueTask ParseAsync(PipeReader reader, TraceSource log = null) + where T : IHttpResponseLineHandler, IHttpHeadersHandler, new() + { + var result = await reader.ReadAsync(); + ReadOnlySequence buffer = result.Buffer; + + if (log != null) log.WriteInformation("RESPONSE: ", buffer.First); + + var handler = new T(); + // TODO (pri 2): this should not be static, or all should be static + if (!HttpParser.ParseResponseLine(ref handler, ref buffer, out int rlConsumed)) + { + throw new NotImplementedException("could not parse the response"); + } + + buffer = buffer.Slice(rlConsumed); + if (!s_headersParser.ParseHeaders(ref handler, buffer, out int hdConsumed)) + { + throw new NotImplementedException("could not parse the response"); + } + + reader.AdvanceTo(buffer.GetPosition(buffer.Start, hdConsumed)); + + return handler; + } + async Task SendAsync() { var reader = _requestPipe.Reader; diff --git a/samples/AzCopyCore/AzCopyCore/StorageRequests.cs b/samples/AzCopyCore/AzCopyCore/StorageRequests.cs index eb7ca47bfc4..c0b9ec651ec 100644 --- a/samples/AzCopyCore/AzCopyCore/StorageRequests.cs +++ b/samples/AzCopyCore/AzCopyCore/StorageRequests.cs @@ -5,6 +5,7 @@ using System.IO; using System.IO.Pipelines; using System.Net.Experimental; +using System.Text; using System.Text.Http.Formatter; using System.Text.Http.Parser; using System.Threading.Tasks; @@ -71,11 +72,12 @@ protected virtual void WriteOtherHeaders(ref BufferWriter writer, ref T argument writer.WriteHeader("Host", arguments.Client.Host); } - // TODO (pri 2): this should be UTF8 - public static string AsString(Http.Method verb) + static readonly byte[] s_GETu8 = Encoding.ASCII.GetBytes("GET"); + static readonly byte[] s_PUTu8 = Encoding.ASCII.GetBytes("PUT"); + public static ReadOnlyMemory AsString(Http.Method verb) { - if (verb == Http.Method.Get) return "GET"; - if (verb == Http.Method.Put) return "PUT"; + if (verb == Http.Method.Get) return s_GETu8; + if (verb == Http.Method.Put) return s_PUTu8; throw new NotImplementedException(); } } diff --git a/samples/AzCopyCore/AzCopyCore/temp/Extensions.cs b/samples/AzCopyCore/AzCopyCore/temp/Extensions.cs index 3965d3f6200..f3f6042b29e 100644 --- a/samples/AzCopyCore/AzCopyCore/temp/Extensions.cs +++ b/samples/AzCopyCore/AzCopyCore/temp/Extensions.cs @@ -2,7 +2,6 @@ using System.Diagnostics; using System.IO; using System.IO.Pipelines; -using System.Text; using System.Text.Http.Parser; using System.Threading.Tasks; @@ -12,9 +11,9 @@ namespace System.Buffers static class GeneralExtensions { /// - /// Copies bytes from ReadOnlyBuffer to a Stream + /// Copies bytes from ReadOnlySequence to a Stream /// - public static async Task WriteAsync(this Stream stream, ReadOnlyBuffer buffer) + public static async Task WriteAsync(this Stream stream, ReadOnlySequence buffer) { for (var position = buffer.Start; buffer.TryGet(ref position, out var memory);) { @@ -30,7 +29,7 @@ public static async Task WriteAsync(this Stream stream, PipeReader reader, ulong while (bytes > 0) { var result = await reader.ReadAsync(); - ReadOnlyBuffer bodyBuffer = result.Buffer; + ReadOnlySequence bodyBuffer = result.Buffer; if (bytes < (ulong)bodyBuffer.Length) { throw new NotImplementedException(); @@ -84,104 +83,6 @@ public static void WriteError(this TraceSource source, string message) } } - public static class HttpExtensions - { - static HttpParser s_headersParser = new HttpParser(); - private const byte ByteLF = (byte)'\n'; - private const byte ByteCR = (byte)'\r'; - private const long maxRequestLineLength = 1024; - static readonly byte[] s_Eol = Encoding.ASCII.GetBytes("\r\n"); - static readonly byte[] s_http11 = Encoding.ASCII.GetBytes("HTTP/1.1"); - static readonly byte[] s_http10 = Encoding.ASCII.GetBytes("HTTP/1.0"); - static readonly byte[] s_http20 = Encoding.ASCII.GetBytes("HTTP/2.0"); - - // TODO (pri 2): move to corfxlab - public static bool ParseResponseLine(ref T handler, ref ReadOnlyBuffer buffer, out int consumedBytes) where T : IHttpResponseLineHandler - { - var line = buffer.First.Span; - var lf = line.IndexOf(ByteLF); - if (lf >= 0) - { - line = line.Slice(0, lf + 1); - } - else if (buffer.IsSingleSegment) - { - consumedBytes = 0; - return false; - } - else - { - long index = Sequence.IndexOf(buffer, ByteLF); - if(index < 0) - { - consumedBytes = 0; - return false; - } - if(index > maxRequestLineLength) - { - throw new Exception("invalid response (LF too far)"); - } - line = line.Slice(0, lf + 1); - } - - if(line[lf - 1] != ByteCR) - { - throw new Exception("invalid response (no CR)"); - } - - Http.Version version; - if (line.StartsWith(s_http11)) { version = Http.Version.Http11; } - // TODO (pri 2): add HTTP2 to HTTP.Version - else if (line.StartsWith(s_http20)) { version = Http.Version.Unknown; } - else if (line.StartsWith(s_http10)) { version = Http.Version.Http10; } - else - { - throw new Exception("invalid response (version)"); - } - - int codeStart = line.IndexOf((byte)' ') + 1; - var codeSlice = line.Slice(codeStart); - if (!Utf8Parser.TryParse(codeSlice, out ushort code, out consumedBytes)) - { - throw new Exception("invalid response (status code)"); - } - - var reasonStart = consumedBytes + 1; - var reason = codeSlice.Slice(reasonStart, codeSlice.Length - reasonStart - 2); - consumedBytes = lf + s_Eol.Length; - - handler.OnStatusLine(version, code, reason); - - return true; - } - - // TODO (pri 3): Add to the platform, but this would require common logging API - public static async ValueTask ParseAsync(PipeReader reader, TraceSource log = null) - where T : IHttpResponseLineHandler, IHttpHeadersHandler, new() - { - var result = await reader.ReadAsync(); - ReadOnlyBuffer buffer = result.Buffer; - - if (log != null) log.WriteInformation("RESPONSE: ", buffer.First); - - var handler = new T(); - if (!ParseResponseLine(ref handler, ref buffer, out int rlConsumed)) - { - throw new NotImplementedException("could not parse the response"); - } - - buffer = buffer.Slice(rlConsumed); - if (!s_headersParser.ParseHeaders(ref handler, buffer, out int hdConsumed)) - { - throw new NotImplementedException("could not parse the response"); - } - - reader.AdvanceTo(buffer.GetPosition(buffer.Start, hdConsumed)); - - return handler; - } - } - // TODO (pri 3): Should I use the command line library? class CommandOptions {