/******************************************************************************* * 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 }