Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Added some APIs needed by my AzCopy Sample #2106

Merged
merged 1 commit into from
Feb 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/System.Azure.Experimental/System/Azure/Key.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,24 @@ public static byte[] ComputeKeyBytes(string key)
}
return keyBytes;
}

public static byte[] ComputeKeyBytes(this ReadOnlySpan<char> key)
{
int size = key.Length * 2;
var buffer = size < 128 ? stackalloc byte[size] : new byte[size];

if (Encodings.Utf16.ToUtf8(key.AsBytes(), buffer, out int consumed, out int written) != OperationStatus.Done)
{
throw new NotImplementedException("need to resize buffer");
}

var keyBytes = new byte[64];
var result = Base64.DecodeFromUtf8(buffer.Slice(0, written), keyBytes, out consumed, out written);
if (result != OperationStatus.Done || written != 64)
{
throw new NotImplementedException("need to resize buffer");
}
return keyBytes;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,71 @@
using System.Binary.Base64Experimental;
using System.Buffers.Text;
using System.Buffers;
using System.Buffers.Transformations;

namespace System.Azure.Authentication
{
public struct StorageAuthorizationHeader : IWritable
{
private static TransformationFormat s_toBase64 = new TransformationFormat(Base64Experimental.BytesToUtf8Encoder);
private static TransformationFormat s_removeCR = new TransformationFormat(new RemoveTransformation(13));

public Sha256 Hash;
public string HttpVerb;
public string AccountName;
public string CanonicalizedResource;
public WritableBytes CanonicalizedHeaders;
public long ContentLength;

public bool TryWrite(Span<byte> buffer, out int written, StandardFormat format = default)
{
try
{
var writer = BufferWriter.Create(buffer);
writer.Write("SharedKey ");
writer.Write(AccountName);
writer.Write(':');

int signatureStart = writer.WrittenCount;

writer.Write(HttpVerb);
if (ContentLength == 0)
{
writer.Write("\n\n\n\n\n\n\n\n\n\n\n\n");
}
else
{
writer.Write("\n\n\n");
writer.Write(ContentLength.ToString());
writer.Write("\n\n\n\n\n\n\n\n\n");
}
writer.WriteBytes(CanonicalizedHeaders, s_removeCR);

// write canonicalized resource
writer.Write('/');
writer.Write(AccountName);
writer.Write('/');
writer.Write(CanonicalizedResource);

// compute hash
Hash.Append(writer.Written.Slice(signatureStart));
writer.WrittenCount = signatureStart;

// write hash
writer.WriteBytes(Hash, s_toBase64);

written = writer.WrittenCount;
return true;
}
catch (BufferWriter.BufferTooSmallException)
{
buffer.Clear();
written = 0;
return false;
}
}
}

public static class StorageAccessSignature
{
private static Utf8String s_emptyHeaders = (Utf8String)"\n\n\n\n\n\n\n\n\n\n\nx-ms-date:"; // this wont be needed once we have UTF8 literals
Expand Down
61 changes: 61 additions & 0 deletions src/System.Buffers.Experimental/RemoveTranstomation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace System.Buffers.Transformations
{
public struct WritableBytes : IWritable
{
readonly ReadOnlyMemory<byte> _bytes;

public WritableBytes(ReadOnlyMemory<byte> bytes)
{
_bytes = bytes;
}

public bool TryWrite(Span<byte> buffer, out int written, StandardFormat format = default)
{
if (format != default) throw new InvalidOperationException();

if (!_bytes.Span.TryCopyTo(buffer))
{
written = 0;
return false;
}
written = _bytes.Length;
return true;
}
}

public class RemoveTransformation : IBufferTransformation
{
byte _value;
public RemoveTransformation(byte valueToRemove)
{
_value = valueToRemove;
}
public OperationStatus Execute(ReadOnlySpan<byte> input, Span<byte> output, out int consumed, out int written)
{
written = 0;
for (consumed = 0; consumed < input.Length; consumed++)
{
if (input[consumed] == _value) continue;
if (written >= output.Length) return OperationStatus.DestinationTooSmall;
output[written++] = input[consumed];
}
return OperationStatus.Done;
}

public OperationStatus Transform(Span<byte> buffer, int dataLength, out int written)
{
written = 0;
for (int consumed = 0; consumed < dataLength; consumed++)
{
if (buffer[consumed] == _value) continue;
if (written >= buffer.Length) return OperationStatus.DestinationTooSmall;
buffer[written++] = buffer[consumed];
}
return OperationStatus.Done;
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System.Buffers.Text
{
public ref partial struct BufferWriter
{
public bool TryWrite(int value, StandardFormat format = default)
{
if (!Utf8Formatter.TryFormat(value, Free, out int written, format)) return false;
_written += written;
return true;
}

public void Write(int value, StandardFormat format = default)
{
while (!TryWrite(value, format)) Resize();
}

public bool TryWriteLine(long value, StandardFormat format = default)
{
if (!Utf8Formatter.TryFormat(value, Free, out int written, format)) return false;
if (!NewLine.TryCopyTo(Free.Slice(written))) return false;
_written += written + NewLine.Length;
return true;
}

public void WriteLine(long value, StandardFormat format = default)
{
while (!TryWriteLine(value, format)) Resize();
}
}
}
25 changes: 24 additions & 1 deletion src/System.Text.Http.Parser/HttpParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Buffers;
using System.Buffers.Text;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
Expand All @@ -22,6 +23,7 @@ public class HttpParser : IHttpParser
private const byte ByteTab = (byte)'\t';
private const byte ByteQuestionMark = (byte)'?';
private const byte BytePercentage = (byte)'%';
static readonly byte[] s_Eol = Encoding.UTF8.GetBytes("\r\n");

private readonly bool _showErrorDetails;

Expand Down Expand Up @@ -76,7 +78,7 @@ public unsafe bool ParseRequestLine<T>(T handler, in ReadOnlyBuffer<byte> buffer
return true;
}

static readonly byte[] s_Eol = Encoding.UTF8.GetBytes("\r\n");

public unsafe bool ParseRequestLine<T>(ref T handler, in ReadOnlyBuffer<byte> buffer, out int consumed) where T : IHttpRequestLineHandler
{
// Prepare the first span
Expand Down Expand Up @@ -475,6 +477,27 @@ public unsafe bool ParseHeaders<T>(ref T handler, ReadOnlyBuffer<byte> buffer, o
}
}

public bool ParseResponseLine<T>(ref T handler, ref ReadOnlyBuffer<byte> buffer, out int consumedBytes) where T : IHttpResponseLineHandler
{
var first = buffer.First.Span;
var eol = first.IndexOf(s_Eol);
if (eol == -1)
{
throw new NotImplementedException();
}
first = first.Slice(0, eol);
int codeStart = first.IndexOf((byte)' ') + 1;
var codeSlice = first.Slice(codeStart);
if (!Utf8Parser.TryParse(codeSlice, out ushort code, out consumedBytes))
{
throw new Exception("no status code");
}

handler.OnStartLine(Parser.Http.Version.Http11, code, codeSlice.Slice(consumedBytes + 1));
consumedBytes = eol + s_Eol.Length;
return true;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void ReadTwoChars(ReadOnlyBuffer<byte> buffer, int consumedBytes, out int ch1, out int ch2)
{
Expand Down
10 changes: 10 additions & 0 deletions src/System.Text.Http.Parser/IHttpResponseLineHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace System.Text.Http.Parser
{
public interface IHttpResponseLineHandler
{
void OnStartLine(Http.Version version, ushort code, ReadOnlySpan<byte> status);
}
}
3 changes: 3 additions & 0 deletions src/System.Text.Http/System.Text.Http.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
<TargetFramework>netstandard1.1</TargetFramework>
<PackageTags>HTTP parser parsing .NET non-allocating corefxlab</PackageTags>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\System.Text.Formatting\System.Text.Formatting.csproj" />
<ProjectReference Include="..\System.Text.Http.Parser\System.Text.Http.Parser.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Buffers;
using System.Buffers.Text;

namespace System.Text.Http.Formatter
{
public static class BufferWriterHttpExtensions
{
public static readonly byte[] s_httpNewline = new byte[] { 13, 10 };
public static readonly byte[] s_eoh = new byte[] { 13, 10, 13, 10 };
static byte[] s_Get = new byte[] { (byte)'G', (byte)'E', (byte)'T' };
static byte[] s_Put = new byte[] { (byte)'P', (byte)'U', (byte)'T' };

public static BufferWriter AsHttpWriter(this Span<byte> buffer)
{
var writer = BufferWriter.Create(buffer);
writer.NewLine = s_httpNewline;
return writer;
}

public static void WriteRequestLine(ref this BufferWriter writer, Parser.Http.Method verb, string path)
{
writer.WriteBytes(verb.AsBytes());
writer.Write(" /");

writer.Write(path);
writer.WriteLine(" HTTP/1.1");
}

public static void WriteHeader(ref this BufferWriter writer, string headerName, string headerValue)
{
writer.Write(headerName);
writer.Write(":");
writer.WriteLine(headerValue);
}

public static void WriteHeader(ref this BufferWriter writer, string headerName, int headerValue)
{
writer.Write(headerName);
writer.Write(":");
// TODO (Pri 0): this allocation needs to be eliminated
writer.WriteLine(headerValue);
}

public static void WriteHeader(ref this BufferWriter writer, string headerName, long headerValue)
{
writer.Write(headerName);
writer.Write(":");

writer.WriteLine(headerValue);
}

public static void WriteHeader(ref this BufferWriter writer, string headerName, DateTime headerValue, StandardFormat format)
{
writer.Write(headerName);
writer.Write(":");
writer.WriteLine(headerValue, format);
}

public static void WriteHeader<T>(ref this BufferWriter writer, string headerName, T headerValue, StandardFormat format)
where T : IWritable
{
writer.Write(headerName);
writer.Write(":");
writer.WriteBytes(headerValue, format);
writer.WriteLine("");
}

public static void WriteEoh(ref this BufferWriter writer)
{
writer.WriteLine("");
}

static ReadOnlySpan<byte> AsBytes(this Parser.Http.Method verb)
{
if (verb == Parser.Http.Method.Get) return s_Get;
if (verb == Parser.Http.Method.Put) return s_Put;
throw new NotImplementedException();
}
}
}