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)}"); } } }
/// <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"); } } } } }