diff --git a/src/System.Azure.Experimental/System/Azure/StorageAccessSignature.cs b/src/System.Azure.Experimental/System/Azure/StorageAccessSignature.cs index 71f13f5d2a0..00b0cf0f925 100644 --- a/src/System.Azure.Experimental/System/Azure/StorageAccessSignature.cs +++ b/src/System.Azure.Experimental/System/Azure/StorageAccessSignature.cs @@ -17,7 +17,7 @@ public struct StorageAuthorizationHeader : IWritable private static TransformationFormat s_removeCR = new TransformationFormat(new RemoveTransformation(13)); public Sha256 Hash; - public string HttpVerb; + public ReadOnlyMemory HttpVerb; public string AccountName; public string CanonicalizedResource; public WritableBytes CanonicalizedHeaders; @@ -34,7 +34,7 @@ public bool TryWrite(Span buffer, out int written, StandardFormat format = int signatureStart = writer.WrittenCount; - writer.Write(HttpVerb); + writer.WriteBytes(HttpVerb); if (ContentLength == 0) { writer.Write("\n\n\n\n\n\n\n\n\n\n\n\n"); @@ -42,7 +42,7 @@ public bool TryWrite(Span buffer, out int written, StandardFormat format = else { writer.Write("\n\n\n"); - writer.Write(ContentLength.ToString()); + writer.Write(ContentLength); writer.Write("\n\n\n\n\n\n\n\n\n"); } writer.WriteBytes(CanonicalizedHeaders, s_removeCR); diff --git a/src/System.Buffers.Experimental/System/Buffers/Text/BufferWriter_ints.cs b/src/System.Buffers.Experimental/System/Buffers/Text/BufferWriter_ints.cs index f54a0377717..08680f9c196 100644 --- a/src/System.Buffers.Experimental/System/Buffers/Text/BufferWriter_ints.cs +++ b/src/System.Buffers.Experimental/System/Buffers/Text/BufferWriter_ints.cs @@ -30,5 +30,17 @@ public void WriteLine(long value, StandardFormat format = default) { while (!TryWriteLine(value, format)) Resize(); } + + public void Write(long value, StandardFormat format = default) + { + while (!TryWrite(value, format)) Resize(); + } + + public bool TryWrite(long value, StandardFormat format = default) + { + if (!Utf8Formatter.TryFormat(value, Free, out int written, format)) return false; + _written += written; + return true; + } } } diff --git a/src/System.Buffers.Experimental/System/Buffers/Text/BufferWriter_writable.cs b/src/System.Buffers.Experimental/System/Buffers/Text/BufferWriter_writable.cs index 6821438e8f0..6757fd46950 100644 --- a/src/System.Buffers.Experimental/System/Buffers/Text/BufferWriter_writable.cs +++ b/src/System.Buffers.Experimental/System/Buffers/Text/BufferWriter_writable.cs @@ -38,7 +38,7 @@ public void WriteBytes(ReadOnlyMemory bytes) #endregion #region IWritable - public bool TryWriteBytes(T value, StandardFormat format) where T : IWritable + public bool TryWriteBytes(T value, StandardFormat format = default) where T : IWritable { if (!value.TryWrite(Free, out int written, format)) { @@ -48,7 +48,7 @@ public bool TryWriteBytes(T value, StandardFormat format) where T : IWritable return true; } - public void WriteBytes(T value, StandardFormat format) where T : IWritable + public void WriteBytes(T value, StandardFormat format = default) where T : IWritable { while (!TryWriteBytes(value, format)) Resize(); } @@ -76,7 +76,7 @@ public void WriteBytes(T value, TransformationFormat format) where T : IWrita #endregion #region IBufferFormattable - public bool TryWrite(T value, StandardFormat format) where T : IBufferFormattable + public bool TryWrite(T value, StandardFormat format = default) where T : IBufferFormattable { if (value.TryFormat(Free, out int written, format, SymbolTable.InvariantUtf8)) { @@ -86,7 +86,7 @@ public bool TryWrite(T value, StandardFormat format) where T : IBufferFormatt return false; } - public void Write(T value, StandardFormat format) where T : IBufferFormattable + public void Write(T value, StandardFormat format = default) where T : IBufferFormattable { while (!TryWrite(value, format)) Resize(); } diff --git a/src/System.Text.Http.Parser/HttpParser.cs b/src/System.Text.Http.Parser/HttpParser.cs index ac259182b4d..8114c4202e7 100644 --- a/src/System.Text.Http.Parser/HttpParser.cs +++ b/src/System.Text.Http.Parser/HttpParser.cs @@ -23,7 +23,12 @@ public class HttpParser : IHttpParser private const byte ByteTab = (byte)'\t'; private const byte ByteQuestionMark = (byte)'?'; private const byte BytePercentage = (byte)'%'; + private const long maxRequestLineLength = 1024; + static readonly byte[] s_Eol = Encoding.UTF8.GetBytes("\r\n"); + static readonly byte[] s_http11 = Encoding.UTF8.GetBytes("HTTP/1.1"); + static readonly byte[] s_http10 = Encoding.UTF8.GetBytes("HTTP/1.0"); + static readonly byte[] s_http20 = Encoding.UTF8.GetBytes("HTTP/2.0"); private readonly bool _showErrorDetails; @@ -476,24 +481,61 @@ public unsafe bool ParseHeaders(ref T handler, ReadOnlySequence buffer, } } - public bool ParseResponseLine(ref T handler, ref ReadOnlySequence buffer, out int consumedBytes) where T : IHttpResponseLineHandler + public static bool ParseResponseLine(ref T handler, ref ReadOnlySequence buffer, out int consumedBytes) where T : IHttpResponseLineHandler { - var first = buffer.First.Span; - var eol = first.IndexOf(s_Eol); - if (eol == -1) + 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 NotImplementedException(); + throw new Exception("invalid response (no CR)"); } - first = first.Slice(0, eol); - int codeStart = first.IndexOf((byte)' ') + 1; - var codeSlice = first.Slice(codeStart); + + Http.Version version; + if (line.StartsWith(s_http11)) { version = Http.Version.Http11; } + else if (line.StartsWith(s_http20)) { version = Http.Version.Http20; } + 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("no status code"); + throw new Exception("invalid response (status code)"); } - handler.OnStatusLine(Http.Version.Http11, code, codeSlice.Slice(consumedBytes + 1)); - consumedBytes = eol + s_Eol.Length; + 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; } diff --git a/src/System.Text.Http.Parser/HttpVersion.cs b/src/System.Text.Http.Parser/HttpVersion.cs index 6f94b0d7c3f..98713307a89 100644 --- a/src/System.Text.Http.Parser/HttpVersion.cs +++ b/src/System.Text.Http.Parser/HttpVersion.cs @@ -10,7 +10,8 @@ public enum Version { Unknown = -1, Http10 = 0, - Http11 = 1 + Http11 = 1, + Http20 = 2, } } }