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

Commit

Permalink
Added some APIs needed by my AzCopy Sample (#2106)
Browse files Browse the repository at this point in the history
  • Loading branch information
KrzysztofCwalina authored Feb 8, 2018
1 parent 1783bd6 commit 9ca2c3b
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 1 deletion.
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();
}
}
}

0 comments on commit 9ca2c3b

Please sign in to comment.