public async Task <int> ReceiveAsync(byte[] buffer, int offset, int size, TimeSpan timeout)
        {
            if (Logging.IsEnabled)
            {
                Logging.Enter(this, timeout, $"{nameof(IotHubClientWebSocket)}.{nameof(ReceiveAsync)}");
            }

            byte[] header = new byte[2];

            Fx.AssertAndThrow(this.State == WebSocketState.Open, ClientWebSocketNotInOpenStateDuringReceive);
            this.TcpClient.ReceiveTimeout = TimeoutHelper.ToMilliseconds(timeout);

            bool succeeded = false;

            try
            {
                byte payloadLength;
                bool pongFrame;

                // TODO: rewrite this section to handle all control frames (including ping)
                int totalBytesRead;
                int bytesRead;
                do
                {
                    // Ignore pong frame and start over
                    totalBytesRead = 0;
                    do
                    {
                        bytesRead = await this.WebSocketStream.ReadAsync(header, totalBytesRead, header.Length - totalBytesRead).ConfigureAwait(false);

                        if (bytesRead == 0)
                        {
                            throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                        }

                        totalBytesRead += bytesRead;
                    }while (totalBytesRead < header.Length);

                    if (!ParseWebSocketFrameHeader(header, out payloadLength, out pongFrame))
                    {
                        // Encountered a close frame or error in parsing frame from server. Close connection
                        var closeHeader = PrepareWebSocketHeader(0, WebSocketMessageType.Close);

                        await this.WebSocketStream.WriteAsync(closeHeader, 0, closeHeader.Length).ConfigureAwait(false);

                        this.State = WebSocketState.Closed;
#if !NETSTANDARD1_3
                        this.WebSocketStream?.Close();
                        this.TcpClient?.Close();
#else
                        this.WebSocketStream?.Dispose();
                        this.TcpClient?.Dispose();
#endif
                        return(0);  // TODO: throw exception?
                    }

                    if (pongFrame && payloadLength > 0)
                    {
                        totalBytesRead = 0;
                        var tempBuffer = new byte[payloadLength];
                        while (totalBytesRead < payloadLength)
                        {
                            bytesRead = await this.WebSocketStream.ReadAsync(tempBuffer, totalBytesRead, payloadLength - totalBytesRead).ConfigureAwait(false);

                            if (bytesRead == 0)
                            {
                                throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                            }

                            totalBytesRead += bytesRead;
                        }
                    }
                }while (pongFrame);

                totalBytesRead = 0;

                if (buffer.Length < payloadLength)
                {
                    throw Fx.Exception.AsError(new InvalidOperationException(Resources.SizeExceedsRemainingBufferSpace));
                }

                if (payloadLength < MediumSizeFrame)
                {
                    while (totalBytesRead < payloadLength)
                    {
                        bytesRead = await this.WebSocketStream.ReadAsync(buffer, offset + totalBytesRead, payloadLength - totalBytesRead).ConfigureAwait(false);

                        if (bytesRead == 0)
                        {
                            throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                        }

                        totalBytesRead += bytesRead;
                    }
                }
                else
                {
                    switch (payloadLength)
                    {
                    case MediumSizeFrame:
                        // read payload length (< 64K)
                        do
                        {
                            bytesRead = await this.WebSocketStream.ReadAsync(header, totalBytesRead, header.Length - totalBytesRead).ConfigureAwait(false);

                            if (bytesRead == 0)
                            {
                                throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                            }

                            totalBytesRead += bytesRead;
                        }while (totalBytesRead < header.Length);

                        totalBytesRead = 0;
                        ushort extendedPayloadLength = (ushort)((header[0] << 8) | header[1]);

                        // read payload
                        if (buffer.Length >= extendedPayloadLength)
                        {
                            while (totalBytesRead < extendedPayloadLength)
                            {
                                bytesRead = await this.WebSocketStream.ReadAsync(buffer, offset + totalBytesRead, extendedPayloadLength - totalBytesRead).ConfigureAwait(false);

                                if (bytesRead == 0)
                                {
                                    throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                                }

                                totalBytesRead += bytesRead;
                            }
                        }
                        else
                        {
                            throw Fx.Exception.AsError(new InvalidOperationException(Resources.SizeExceedsRemainingBufferSpace));
                        }

                        break;

                    case LargeSizeFrame:
                        // read payload length (>= 64K)
                        var payloadLengthBuffer = new byte[8];
                        do
                        {
                            bytesRead = await this.WebSocketStream.ReadAsync(payloadLengthBuffer, totalBytesRead, payloadLengthBuffer.Length - totalBytesRead).ConfigureAwait(false);

                            if (bytesRead == 0)
                            {
                                throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                            }

                            totalBytesRead += bytesRead;
                        }while (totalBytesRead < payloadLengthBuffer.Length);

                        totalBytesRead = 0;

                        // ignore bytes 0-3 - length cannot be larger than a 32-bit number
                        uint superExtendedPayloadLength = (uint)((payloadLengthBuffer[4] << 24) | (payloadLengthBuffer[5] << 16) | (payloadLengthBuffer[6] << 8) | payloadLengthBuffer[7]);

                        // read payload
                        if (buffer.Length >= superExtendedPayloadLength)
                        {
                            while (totalBytesRead < superExtendedPayloadLength)
                            {
                                bytesRead = await this.WebSocketStream.ReadAsync(buffer, offset + totalBytesRead, (int)(superExtendedPayloadLength - totalBytesRead)).ConfigureAwait(false);

                                if (bytesRead == 0)
                                {
                                    throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                                }

                                totalBytesRead += bytesRead;
                            }
                        }
                        else
                        {
                            throw Fx.Exception.AsError(new InvalidOperationException(Resources.SizeExceedsRemainingBufferSpace));
                        }

                        break;
                    }
                }

                succeeded = true;
                return(totalBytesRead);
            }
            finally
            {
                if (!succeeded)
                {
                    this.Fault();
                }
                if (Logging.IsEnabled)
                {
                    Logging.Exit(this, timeout, $"{nameof(IotHubClientWebSocket)}.{nameof(ReceiveAsync)}");
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Copies the properties from the amqp message to the Message instance.
        /// </summary>
        public static void UpdateMessageHeaderAndProperties(AmqpMessage amqpMessage, Message data)
        {
            Fx.AssertAndThrow(amqpMessage.DeliveryTag != null, "AmqpMessage should always contain delivery tag.");
            data.DeliveryTag = amqpMessage.DeliveryTag;

            SectionFlag sections = amqpMessage.Sections;

            if ((sections & SectionFlag.Properties) != 0)
            {
                // Extract only the Properties that we support
                data.MessageId = amqpMessage.Properties.MessageId != null?amqpMessage.Properties.MessageId.ToString() : null;

                data.To = amqpMessage.Properties.To != null?amqpMessage.Properties.To.ToString() : null;

                if (amqpMessage.Properties.AbsoluteExpiryTime.HasValue)
                {
                    data.ExpiryTimeUtc = amqpMessage.Properties.AbsoluteExpiryTime.Value;
                }

                data.CorrelationId = amqpMessage.Properties.CorrelationId != null?amqpMessage.Properties.CorrelationId.ToString() : null;

                data.UserId = amqpMessage.Properties.UserId.Array != null?Encoding.UTF8.GetString(amqpMessage.Properties.UserId.Array, 0 /*index*/, amqpMessage.Properties.UserId.Array.Length) : null;
            }

            if ((sections & SectionFlag.MessageAnnotations) != 0)
            {
                string lockToken;
                if (amqpMessage.MessageAnnotations.Map.TryGetValue(LockTokenName, out lockToken))
                {
                    data.LockToken = lockToken;
                }

                ulong sequenceNumber;
                if (amqpMessage.MessageAnnotations.Map.TryGetValue(SequenceNumberName, out sequenceNumber))
                {
                    data.SequenceNumber = sequenceNumber;
                }

                DateTime enqueuedTime;
                if (amqpMessage.MessageAnnotations.Map.TryGetValue(MessageSystemPropertyNames.EnqueuedTime, out enqueuedTime))
                {
                    data.EnqueuedTimeUtc = enqueuedTime;
                }

                byte deliveryCount;
                if (amqpMessage.MessageAnnotations.Map.TryGetValue(MessageSystemPropertyNames.DeliveryCount, out deliveryCount))
                {
                    data.DeliveryCount = deliveryCount;
                }
            }

            if ((sections & SectionFlag.ApplicationProperties) != 0)
            {
                foreach (KeyValuePair <MapKey, object> pair in amqpMessage.ApplicationProperties.Map)
                {
                    object netObject = null;
                    if (TryGetNetObjectFromAmqpObject(pair.Value, MappingType.ApplicationProperty, out netObject))
                    {
                        var stringObject = netObject as string;

                        if (stringObject != null)
                        {
                            switch (pair.Key.ToString())
                            {
                            case MessageSystemPropertyNames.Operation:
                                data.SystemProperties[pair.Key.ToString()] = stringObject;
                                break;

                            default:
                                data.Properties[pair.Key.ToString()] = stringObject;
                                break;
                            }
                        }
                        else
                        {
                            // TODO: RDBug 4093369 Handling of non-string property values in Amqp messages
                            // Drop non-string properties and log an error
                            Fx.Exception.TraceHandled(new InvalidDataException("IotHub does not accept non-string Amqp properties"), "MessageConverter.UpdateMessageHeaderAndProperties");
                        }
                    }
                }
            }
        }