/*******************************************************************************
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use
* this file except in compliance with the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file.
* This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
* *****************************************************************************
* __ _ _ ___
* ( )( \/\/ )/ __)
* /__\ \ / \__ \
* (_)(_) \/\/ (___/
*
* AWS SDK for .NET
*
*/
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net;
using System.Text;
using Amazon.Runtime.Internal.Util;
namespace Amazon.Runtime.EventStreams
{
#region headerParser
///
/// Supported Header types for vnd.amazon.event-stream
///
[SuppressMessage("Microsoft.Design", "CA1028", Justification = "Low-level memory usage")]
public enum EventStreamHeaderType : byte
{
BoolTrue = 0,
BoolFalse,
Byte,
Int16,
Int32,
Int64,
ByteBuf,
String,
Timestamp,
UUID
}
public interface IEventStreamHeader
{
///
/// Name for the Header. Maximum of 255 bytes.
///
string Name { get; }
///
/// Header type id
///
EventStreamHeaderType HeaderType { get; }
///
/// Computes the amount of memory neccesary to serialize this Header.
///
[SuppressMessage("Microsoft.Design", "CA1024", Justification = "Method can throw an exception.")]
int GetWireSize();
///
/// Writes this Header to buffer starting at offset
/// Keep in mind, this API assumes buffer is large enough
/// for the operation.
///
/// buffer to serialize this Header to
/// offset to begin writing at.
///
/// the new offset.
///
int WriteToBuffer(byte[] buffer, int offset);
///
/// Returns the current value as a bool
///
[SuppressMessage("Microsoft.Naming", "CA1720", Justification = "Current name is most descriptive.")]
bool AsBool();
///
/// Sets the current value
///
void SetBool(bool value);
///
/// Returns the current value as a byte
///
Byte AsByte();
///
/// Sets the current value
///
void SetByte(Byte value);
///
/// Gets the current value as a 16 bit integer. (Host Order).
///
///
Int16 AsInt16();
///
/// Sets the current value. (Host Order)
///
void SetInt16(Int16 value);
///
/// Returns the current value as a 32 bit integer. (Host Order)
///
Int32 AsInt32();
///
/// Sets the current value
///
void SetInt32(Int32 value);
///
/// returns the current value as a 64-bit integer. (Host Order)
///
Int64 AsInt64();
///
/// Sets the current value. (Host Order)
///
void SetInt64(Int64 value);
///
/// Returns the current value as a byte buffer.
///
///
byte[] AsByteBuf();
///
/// Sets the current value. Max length is 2^15 - 1
///
void SetByteBuf(byte[] value);
///
/// Returns the current value as a utf-8 string.
///
string AsString();
///
/// Sets the current value. Utf-8 encoded. Max byte size is 2^16 - 1
///
void SetString(string value);
///
/// Gets the current value as a DateTime.
/// Note: You do not need to compensate for unix epoch on this API.
///
///
DateTime AsTimestamp();
///
/// Sets the current value.
/// Note: You do not need to compensate for unix epoch on this API.
///
void SetTimestamp(DateTime value);
///
/// Returns the current value as a Guid (UUID)
///
Guid AsUUID();
///
/// Sets the current value
///
void SetUUID(Guid value);
}
///
/// Header Data for an EventStreamMessage
///
/// Each header is of format:
/// [name len] (1) | [utf-8 name] (v)
/// [type (1)]
/// [value (v)]
///
public class EventStreamHeader : IEventStreamHeader
{
private static readonly DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private const int _sizeOfByte = 1;
private const int _sizeOfInt16 = 2;
private const int _sizeOfInt32 = 4;
private const int _sizeOfInt64 = 8;
private const int _sizeOfGuid = 16;
///
/// Name for the Header. Maximum of 255 bytes.
///
public string Name { get; }
///
/// Header type id
///
public EventStreamHeaderType HeaderType { get; set; }
private Object HeaderValue { get; set; }
///
/// Initializes this Header with name.
///
/// utf-8 string, max length is 255
public EventStreamHeader(string name)
{
this.Name = name;
}
///
/// Deserializes the header in buffer.
///
/// buffer constaining the header
/// index to start from in the buffer.
/// updated offset with new value for offset
[SuppressMessage("Microsoft.Design", "CA1045", Justification = "Performance and Cross-Runtime compatability.")]
public static EventStreamHeader FromBuffer(byte[] buffer, int offset, ref int newOffset)
{
newOffset = offset;
byte nameLength = buffer[newOffset++];
var header = new EventStreamHeader(Encoding.UTF8.GetString(buffer, newOffset, nameLength));
newOffset += nameLength;
header.HeaderType = (EventStreamHeaderType)buffer[newOffset++];
Int16 valueLength = 0;
switch (header.HeaderType)
{
case EventStreamHeaderType.BoolTrue:
header.HeaderValue = true;
break;
case EventStreamHeaderType.BoolFalse:
header.HeaderValue = false;
break;
case EventStreamHeaderType.Byte:
header.HeaderValue = buffer[newOffset];
newOffset += _sizeOfByte;
break;
case EventStreamHeaderType.Int16:
header.HeaderValue = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(buffer, newOffset));
newOffset += _sizeOfInt16;
break;
case EventStreamHeaderType.Int32:
header.HeaderValue = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buffer, newOffset));
newOffset += _sizeOfInt32;
break;
case EventStreamHeaderType.Int64:
header.HeaderValue = IPAddress.NetworkToHostOrder(BitConverter.ToInt64(buffer, newOffset));
newOffset += _sizeOfInt64;
break;
case EventStreamHeaderType.ByteBuf:
valueLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(buffer, newOffset));
newOffset += _sizeOfInt16;
header.HeaderValue = new byte[valueLength];
Buffer.BlockCopy(buffer, newOffset, header.HeaderValue as byte[], 0, valueLength);
newOffset += valueLength;
break;
case EventStreamHeaderType.String:
valueLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(buffer, newOffset));
newOffset += _sizeOfInt16;
header.HeaderValue = Encoding.UTF8.GetString(buffer, newOffset, valueLength);
newOffset += valueLength;
break;
case EventStreamHeaderType.Timestamp:
Int64 tempValue = IPAddress.NetworkToHostOrder(BitConverter.ToInt64(buffer, newOffset));
newOffset += _sizeOfInt64;
//keep in mind on the windows APIs (and hence NetStandard as well) the epoch is 1/1/1900,
//and we're using unix epoch. So we compensate here.
header.HeaderValue = _unixEpoch.AddMilliseconds(tempValue);
break;
case EventStreamHeaderType.UUID:
var guidCpy = new byte[16];
valueLength = _sizeOfGuid;
Buffer.BlockCopy(buffer, newOffset, guidCpy, 0, valueLength);
newOffset += valueLength;
header.HeaderValue = new Guid(guidCpy);
break;
default:
throw new EventStreamParseException(string.Format(CultureInfo.InvariantCulture, "Header Type: {0} is an unknown type.", header.HeaderType));
}
return header;
}
///
/// Writes this Header to buffer starting at offset
/// Keep in mind, this API assumes buffer is large enough
/// for the operation.
///
/// buffer to serialize this Header to
/// offset to begin writing at.
///
/// the new offset.
///
public int WriteToBuffer(byte[] buffer, int offset)
{
var newOffset = offset;
buffer[newOffset++] = (byte)Name.Length;
var nameBytes = Encoding.UTF8.GetBytes(Name);
Buffer.BlockCopy(nameBytes, 0, buffer, newOffset, Name.Length);
newOffset += Name.Length;
buffer[newOffset++] = (byte)HeaderType;
byte[] serializedBytes = null;
int valueLength = 0;
switch (HeaderType)
{
case EventStreamHeaderType.BoolTrue:
case EventStreamHeaderType.BoolFalse:
break;
case EventStreamHeaderType.Byte:
buffer[newOffset++] = (byte)HeaderValue;
break;
case EventStreamHeaderType.Int16:
serializedBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((Int16)HeaderValue));
Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, 2);
newOffset += _sizeOfInt16;
break;
case EventStreamHeaderType.Int32:
serializedBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((Int32)HeaderValue));
Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, 4);
newOffset += _sizeOfInt32;
break;
case EventStreamHeaderType.Int64:
serializedBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((Int64)HeaderValue));
Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, 8);
newOffset += _sizeOfInt64;
break;
case EventStreamHeaderType.ByteBuf:
serializedBytes = HeaderValue as byte[];
valueLength = serializedBytes.Length;
Buffer.BlockCopy(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((Int16)valueLength)), 0,
buffer, newOffset, 2);
newOffset += _sizeOfInt16;
Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, valueLength);
newOffset += valueLength;
break;
case EventStreamHeaderType.String:
serializedBytes = Encoding.UTF8.GetBytes(HeaderValue as string);
valueLength = serializedBytes.Length;
Buffer.BlockCopy(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((Int16)valueLength)), 0,
buffer, newOffset, 2);
newOffset += _sizeOfInt16;
Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, valueLength);
newOffset += valueLength;
break;
case EventStreamHeaderType.Timestamp:
var tempValue = (Int64)((DateTime)HeaderValue).Subtract(_unixEpoch).TotalMilliseconds;
serializedBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(tempValue));
Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, 8);
newOffset += _sizeOfInt64;
break;
case EventStreamHeaderType.UUID:
serializedBytes = ((Guid)HeaderValue).ToByteArray();
Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, serializedBytes.Length);
newOffset += serializedBytes.Length;
break;
default:
throw new EventStreamParseException(string.Format(CultureInfo.InvariantCulture, "Header Type: {0} is an unknown type.", HeaderType));
}
return newOffset;
}
///
/// Computes the amount of memory neccesary to serialize this Header.
///
public int GetWireSize()
{
//each header is of format:
// [name len] (1) | [utf-8 name] (v)
// [type (1)]
// [value (v)]
var len = _sizeOfByte + Name.Length + _sizeOfByte;
switch (HeaderType)
{
case EventStreamHeaderType.BoolTrue:
case EventStreamHeaderType.BoolFalse:
break;
case EventStreamHeaderType.Byte:
len += _sizeOfByte;
break;
case EventStreamHeaderType.Int16:
len += _sizeOfInt16;
break;
case EventStreamHeaderType.Int32:
len += _sizeOfInt32;
break;
case EventStreamHeaderType.Int64:
len += _sizeOfInt64;
break;
case EventStreamHeaderType.ByteBuf:
var bbuf = HeaderValue as byte[];
len += _sizeOfInt16 + bbuf.Length; //2 byte len | buffer
break;
case EventStreamHeaderType.String:
var strLen = Encoding.UTF8.GetBytes(HeaderValue as string).Length;
len += _sizeOfInt16 + strLen; //2 byte len | utf-8 buffer
break;
case EventStreamHeaderType.Timestamp:
len += _sizeOfInt64;
break;
case EventStreamHeaderType.UUID:
len += _sizeOfGuid;
break;
default:
throw new EventStreamParseException(string.Format(CultureInfo.InvariantCulture, "Header Type: {0} is an unknown type.", HeaderType));
}
return len;
}
///
/// Returns the current value as a bool
///
[SuppressMessage("Microsoft.Naming", "CA1720", Justification = "Current name is most descriptive.")]
public bool AsBool()
{
return HeaderType == EventStreamHeaderType.BoolTrue;
}
///
/// Sets the current value
///
public void SetBool(bool value)
{
HeaderValue = value;
HeaderType = value ? EventStreamHeaderType.BoolTrue : EventStreamHeaderType.BoolFalse;
}
///
/// Returns the current value as a byte
///
public Byte AsByte()
{
return (Byte)HeaderValue;
}
///
/// Sets the current value
///
public void SetByte(Byte value)
{
HeaderValue = value;
HeaderType = EventStreamHeaderType.Byte;
}
///
/// Gets the current value as a 16 bit integer. (Host Order).
///
///
public Int16 AsInt16()
{
return (Int16)HeaderValue;
}
///
/// Sets the current value. (Host Order)
///
public void SetInt16(Int16 value)
{
HeaderValue = value;
HeaderType = EventStreamHeaderType.Int16;
}
///
/// Returns the current value as a 32 bit integer. (Host Order)
///
public Int32 AsInt32()
{
return (Int32)HeaderValue;
}
///
/// Sets the current value
///
public void SetInt32(Int32 value)
{
HeaderValue = value;
HeaderType = EventStreamHeaderType.Int32;
}
///
/// returns the current value as a 64-bit integer. (Host Order)
///
public Int64 AsInt64()
{
return (Int64)HeaderValue;
}
///
/// Sets the current value. (Host Order)
///
public void SetInt64(Int64 value)
{
HeaderValue = value;
HeaderType = EventStreamHeaderType.Int64;
}
///
/// Returns the current value as a byte buffer.
///
///
public byte[] AsByteBuf()
{
return HeaderValue as byte[];
}
///
/// Sets the current value. Max length is 2^15 - 1
///
public void SetByteBuf(byte[] value)
{
HeaderValue = value;
HeaderType = EventStreamHeaderType.ByteBuf;
}
///
/// Returns the current value as a utf-8 string.
///
public string AsString()
{
return HeaderValue as string;
}
///
/// Sets the current value. Utf-8 encoded. Max byte size is 2^16 - 1
///
public void SetString(string value)
{
HeaderValue = value;
HeaderType = EventStreamHeaderType.String;
}
///
/// Gets the current value as a DateTime.
/// Note: You do not need to compensate for unix epoch on this API.
///
///
public DateTime AsTimestamp()
{
return (DateTime)HeaderValue;
}
///
/// Sets the current value.
/// Note: You do not need to compensate for unix epoch on this API.
///
public void SetTimestamp(DateTime value)
{
HeaderValue = value;
HeaderType = EventStreamHeaderType.Timestamp;
}
///
/// Returns the current value as a Guid (UUID)
///
public Guid AsUUID()
{
return (Guid)HeaderValue;
}
///
/// Sets the current value
///
public void SetUUID(Guid value)
{
HeaderValue = value;
HeaderType = EventStreamHeaderType.UUID;
}
}
#endregion
}