Пример #1
0
        public static byte[] ConvertToMqtt(MqttSession session, EventMessage message)
        {
            if (message.Protocol == ProtocolType.MQTT)
            {
                return(MqttConversion(session, message.Message));
            }

            if (message.Protocol == ProtocolType.COAP)
            {
                CoapMessage msg  = CoapMessage.DecodeMessage(message.Message);
                CoapUri     curi = new CoapUri(msg.ResourceUri.ToString());
                QualityOfServiceLevelType qos = QualityOfServiceLevelType.AtLeastOnce;

                QualityOfServiceLevelType?qosType = session.GetQoS(curi.Resource);
                qos = qosType ?? QualityOfServiceLevelType.AtLeastOnce;

                PublishMessage pub = new PublishMessage(false, qos, false, session.NewId(), curi.Resource, msg.Payload);
                return(pub.Encode());
            }

            if (message.Protocol == ProtocolType.REST)
            {
                PublishMessage pubm = new PublishMessage(false, session.GetQoS(message.ResourceUri).Value, false,
                                                         session.NewId(), message.ResourceUri, message.Message);
                return(pubm.Encode());
            }

            return(MqttConversion(session, message.Message));
        }
        public async Task <CoapMessage> PutAsync(CoapMessage message)
        {
            CoapUri             uri = new CoapUri(message.ResourceUri.ToString());
            ResponseMessageType rmt = message.MessageType == CoapMessageType.Confirmable ? ResponseMessageType.Acknowledgement : ResponseMessageType.NonConfirmable;

            //EventValidator.Validate(false, resourceUriString, Channel, graphManager, context).Validated
            //if (!await adapter.CanSubscribeAsync(uri.Resource, channel.IsEncrypted))
            if (EventValidator.Validate(false, uri.Resource, channel, graphManager).Validated)
            {
                return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Unauthorized, message.Token));
            }

            if (coapObserved.ContainsKey(uri.Resource) || coapUnobserved.Contains(uri.Resource))
            {
                //resource previously subscribed
                return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.NotAcceptable, message.Token));
            }

            //this point the resource is not being observed, so we can
            // #1 subscribe to it
            // #2 add to unobserved resources (means not coap observed)

            SubscriptionMetadata metadata = new SubscriptionMetadata()
            {
                IsEphemeral = true,
                Identity    = session.Identity,
                Indexes     = session.Indexes
            };

            string subscriptionUriString = await adapter.SubscribeAsync(uri.Resource, metadata);

            coapUnobserved.Add(uri.Resource);

            return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Created, message.Token));
        }
Пример #3
0
        public async Task <CoapMessage> DeleteAsync(CoapMessage message)
        {
            Exception error = null;

            CoapUri uri = new CoapUri(message.ResourceUri.ToString());

            try
            {
                await adapter.UnsubscribeAsync(uri.Resource);

                coapObserved.Remove(uri.Resource);
            }
            catch (Exception ex)
            {
                Trace.TraceError("{0} - CoAP Delete fault '{1}' ", DateTime.UtcNow.ToString("yyyy-MM-ddTHH-MM-ss.fffff"), ex.Message);
                error = ex;
            }

            if (error == null)
            {
                ResponseMessageType rmt = message.MessageType == CoapMessageType.Confirmable ? ResponseMessageType.Acknowledgement : ResponseMessageType.NonConfirmable;
                return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Deleted, message.Token));
            }
            else
            {
                return(new CoapResponse(message.MessageId, ResponseMessageType.Reset, ResponseCodeType.EmptyMessage));
            }
        }
        public async Task <CoapMessage> ObserveAsync(CoapMessage message)
        {
            if (!message.Observe.HasValue)
            {
                //RST because GET needs to be observe/unobserve
                await logger?.LogWarningAsync($"CoAP observe received without Observe flag and will return RST for {session.Identity}");

                await logger?.LogDebugAsync($"Returning RST because GET needs to be observe/unobserve for {session.Identity}");

                return(new CoapResponse(message.MessageId, ResponseMessageType.Reset, ResponseCodeType.EmptyMessage));
            }

            CoapUri             uri = new CoapUri(message.ResourceUri.ToString());
            ResponseMessageType rmt = message.MessageType == CoapMessageType.Confirmable ? ResponseMessageType.Acknowledgement : ResponseMessageType.NonConfirmable;

            ValidatorResult result = EventValidator.Validate(false, uri.Resource, channel, graphManager);

            if (!result.Validated)
            {
                await logger?.LogErrorAsync($"{result.ErrorMessage} for {session.Identity}");

                return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Unauthorized, message.Token));
            }

            if (!message.Observe.Value)
            {
                //unsubscribe
                await logger?.LogInformationAsync($"CoAP unobserve '{message.ResourceUri.ToString()}' for {session.Identity}.");

                await adapter.UnsubscribeAsync(uri.Resource);

                await logger?.LogDebugAsync($"CoAP unsubscribed '{message.ResourceUri.ToString()} for {session.Identity}'.");

                coapObserved.Remove(uri.Resource);
            }
            else
            {
                //subscribe
                SubscriptionMetadata metadata = new SubscriptionMetadata()
                {
                    IsEphemeral = true,
                    Identity    = session.Identity,
                    Indexes     = session.Indexes
                };

                await logger?.LogInformationAsync($"CoAP subscribed '{message.ResourceUri.ToString()}' for {session.Identity}");

                string subscriptionUriString = await adapter.SubscribeAsync(uri.Resource, metadata);


                if (!coapObserved.ContainsKey(uri.Resource)) //add resource to observed list
                {
                    coapObserved.Add(uri.Resource, message.Token);
                    await logger?.LogDebugAsync("Key added to observable resource.");
                }
            }

            return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Valid, message.Token));
        }
Пример #5
0
        public static byte[] ConvertToCoap(CoapSession session, EventMessage message, byte[] observableToken = null)
        {
            CoapMessage coapMessage = null;
            CoapToken   token       = CoapToken.Create();

            ushort id = observableToken == null?session.CoapSender.NewId(token.TokenBytes) : session.CoapSender.NewId(observableToken);

            string uriString = CoapUri.Create(session.Config.Authority, message.ResourceUri, IsEncryptedChannel);

            if (message.Protocol == ProtocolType.MQTT)
            {
                MqttMessage    msg = MqttMessage.DecodeMessage(message.Message);
                PublishMessage pub = msg as PublishMessage;
                MqttUri        uri = new MqttUri(pub.Topic);
                if (observableToken == null)
                {
                    RequestMessageType messageType = msg.QualityOfService == QualityOfServiceLevelType.AtMostOnce ? RequestMessageType.NonConfirmable : RequestMessageType.Confirmable;
                    //request
                    coapMessage = new CoapRequest(id, messageType, MethodType.POST, new Uri(uriString), MediaTypeConverter.ConvertToMediaType(message.ContentType));
                }
                else
                {
                    //response
                    coapMessage = new CoapResponse(id, ResponseMessageType.NonConfirmable, ResponseCodeType.Content, observableToken, MediaTypeConverter.ConvertToMediaType(uri.ContentType), msg.Payload);
                }
            }
            else if (message.Protocol == ProtocolType.COAP)
            {
                CoapMessage msg = CoapMessage.DecodeMessage(message.Message);
                if (observableToken == null)
                {
                    //request
                    coapMessage = new CoapRequest(id, msg.MessageType == CoapMessageType.Confirmable ? RequestMessageType.Confirmable : RequestMessageType.NonConfirmable, MethodType.POST, new Uri(uriString), MediaTypeConverter.ConvertToMediaType(message.ContentType), msg.Payload);
                }
                else
                {
                    //response
                    coapMessage = new CoapResponse(id, ResponseMessageType.NonConfirmable, ResponseCodeType.Content, observableToken, MediaTypeConverter.ConvertToMediaType(message.ContentType), msg.Payload);
                }
            }
            else
            {
                if (observableToken == null)
                {
                    //request
                    coapMessage = new CoapRequest(id, RequestMessageType.NonConfirmable, MethodType.POST, new Uri(uriString), MediaTypeConverter.ConvertToMediaType(message.ContentType), message.Message);
                }
                else
                {
                    //response
                    coapMessage = new CoapResponse(id, ResponseMessageType.NonConfirmable, ResponseCodeType.Content, observableToken, MediaTypeConverter.ConvertToMediaType(message.ContentType), message.Message);
                }
            }

            return(coapMessage.Encode());
        }
Пример #6
0
        public async Task <CoapMessage> PostAsync(CoapMessage message)
        {
            try
            {
                CoapUri             uri      = new CoapUri(message.ResourceUri.ToString());
                ResponseMessageType rmt      = message.MessageType == CoapMessageType.Confirmable ? ResponseMessageType.Acknowledgement : ResponseMessageType.NonConfirmable;
                EventMetadata       metadata = await GraphManager.GetPiSystemMetadataAsync(uri.Resource);

                if (!await adapter.CanPublishAsync(metadata, channel.IsEncrypted))
                {
                    if (metadata.Audit)
                    {
                        await auditor?.WriteAuditRecordAsync(new MessageAuditRecord("XXXXXXXXXXXX", session.Identity, this.channel.TypeId, "COAP", message.Payload.Length, MessageDirectionType.In, false, DateTime.UtcNow, "Not authorized, missing resource metadata, or channel encryption requirements"));
                    }

                    return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Unauthorized, message.Token));
                }

                string contentType = message.ContentType.HasValue ? message.ContentType.Value.ConvertToContentType() : "application/octet-stream";

                EventMessage msg = new EventMessage(contentType, uri.Resource, ProtocolType.COAP, message.Encode(), DateTime.UtcNow, metadata.Audit);

                if (!string.IsNullOrEmpty(uri.CacheKey))
                {
                    msg.CacheKey = uri.CacheKey;
                }

                if (uri.Indexes == null)
                {
                    await adapter.PublishAsync(msg);
                }
                else
                {
                    List <KeyValuePair <string, string> > indexes = new List <KeyValuePair <string, string> >(uri.Indexes);

                    Task task = Retry.ExecuteAsync(async() =>
                    {
                        await adapter.PublishAsync(msg, indexes);
                    });

                    task.LogExceptions();
                }


                return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Created, message.Token));
            }
            catch (Exception ex)
            {
                Trace.TraceError("{0} - CoAP publish error on channel '{1}'", DateTime.UtcNow.ToString("yyyy-MM-ddTHH-MM-ss.fffff"), channel.Id);
                throw ex;
            }
        }
        public Task <CoapMessage> PostAsync(CoapMessage message)
        {
            TaskCompletionSource <CoapMessage> tcs = new TaskCompletionSource <CoapMessage>();
            CoapUri             uri = new CoapUri(message.ResourceUri.ToString());
            ResponseMessageType rmt = message.MessageType == CoapMessageType.Confirmable ? ResponseMessageType.Acknowledgement : ResponseMessageType.NonConfirmable;


            registry.GetAction("POST", uri.Resource)?.Invoke(MediaTypeConverter.ConvertFromMediaType(message.ContentType), message.Payload);
            CoapMessage response = new CoapResponse(message.MessageId, rmt, ResponseCodeType.Created, message.Token);

            tcs.SetResult(response);
            return(tcs.Task);
        }
Пример #8
0
        public void EnsureAuthentication(CoapMessage message, bool force = false)
        {
            if (!IsAuthenticated || force)
            {
                CoapUri coapUri = new CoapUri(message.ResourceUri.ToString());
                if (!Authenticate(coapUri.TokenType, coapUri.SecurityToken))
                {
                    throw new SecurityException("CoAP session not authenticated.");
                }

                IdentityDecoder decoder = new IdentityDecoder(Config.IdentityClaimType, context, Config.Indexes);
                Identity = decoder.Id;
                Indexes  = decoder.Indexes;
            }
        }
        public async Task <CoapMessage> PostAsync(CoapMessage message)
        {
            try
            {
                CoapUri             uri      = new CoapUri(message.ResourceUri.ToString());
                ResponseMessageType rmt      = message.MessageType == CoapMessageType.Confirmable ? ResponseMessageType.Acknowledgement : ResponseMessageType.NonConfirmable;
                EventMetadata       metadata = await graphManager.GetPiSystemMetadataAsync(uri.Resource);

                ValidatorResult result = EventValidator.Validate(true, metadata, null, graphManager);

                if (!result.Validated)
                {
                    if (metadata.Audit)
                    {
                        await auditor?.WriteAuditRecordAsync(new MessageAuditRecord("XXXXXXXXXXXX", session.Identity, this.channel.TypeId, "COAP", message.Payload.Length, MessageDirectionType.In, false, DateTime.UtcNow, "Not authorized, missing resource metadata, or channel encryption requirements")).LogExceptions(logger);
                    }

                    logger?.LogErrorAsync(result.ErrorMessage);
                    return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Unauthorized, message.Token));
                }

                string contentType = message.ContentType.HasValue ? message.ContentType.Value.ConvertToContentType() : "application/octet-stream";

                EventMessage msg = new EventMessage(contentType, uri.Resource, ProtocolType.COAP, message.Encode(), DateTime.UtcNow, metadata.Audit);

                if (!string.IsNullOrEmpty(uri.CacheKey))
                {
                    msg.CacheKey = uri.CacheKey;
                }

                if (uri.Indexes == null)
                {
                    await adapter.PublishAsync(msg);
                }
                else
                {
                    List <KeyValuePair <string, string> > indexes = GetIndexes(uri);
                    await adapter.PublishAsync(msg, indexes);
                }

                return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Created, message.Token));
            }
            catch (Exception ex)
            {
                logger?.LogErrorAsync(ex, $"CoAP POST fault for {session.Identity}.");
                throw ex;
            }
        }
Пример #10
0
        private void Channel_OnOpen(object sender, ChannelOpenEventArgs e)
        {
            session.IsAuthenticated = Channel.IsAuthenticated;
            logger?.LogDebugAsync(
                $"CoAP protocol channel opening with session authenticated '{session.IsAuthenticated}'.").GetAwaiter();

            try
            {
                if (!Channel.IsAuthenticated && e.Message != null)
                {
                    CoapMessage msg     = CoapMessage.DecodeMessage(e.Message);
                    CoapUri     coapUri = new CoapUri(msg.ResourceUri.ToString());
                    session.IsAuthenticated = session.Authenticate(coapUri.TokenType, coapUri.SecurityToken);
                    logger?.LogDebugAsync(
                        $"CoAP protocol channel opening session authenticated '{session.IsAuthenticated}' by authenticator.")
                    .GetAwaiter();
                }

                if (session.IsAuthenticated)
                {
                    IdentityDecoder decoder = new IdentityDecoder(session.Config.IdentityClaimType, context,
                                                                  session.Config.Indexes);
                    session.Identity = decoder.Id;
                    session.Indexes  = decoder.Indexes;
                    logger?.LogDebugAsync($"CoAP protocol channel opening with session identity '{session.Identity}'.")
                    .GetAwaiter();

                    UserAuditRecord record = new UserAuditRecord(Channel.Id, session.Identity,
                                                                 session.Config.IdentityClaimType, Channel.TypeId, "COAP", "Granted", DateTime.UtcNow);
                    userAuditor?.WriteAuditRecordAsync(record).Ignore();
                }
            }
            catch (Exception ex)
            {
                logger?.LogErrorAsync(ex, $"CoAP adapter opening channel '{Channel.Id}'.").GetAwaiter();
                OnError?.Invoke(this, new ProtocolAdapterErrorEventArgs(Channel.Id, ex));
            }

            if (!session.IsAuthenticated && e.Message != null)
            {
                logger?.LogWarningAsync("CoAP adpater closing due to unauthenticated user.");
                Channel.CloseAsync().Ignore();
            }
            else
            {
                dispatcher = new CoapRequestDispatcher(session, Channel, config, graphManager, logger);
            }
        }
Пример #11
0
        public async Task <CoapMessage> ObserveAsync(CoapMessage message)
        {
            if (!message.Observe.HasValue)
            {
                //RST because GET needs to be observe/unobserve
                Trace.TraceWarning("{0} - CoAP observe received without Observe flag set on channel '{1}', returning RST", DateTime.UtcNow.ToString("yyyy-MM-ddTHH-MM-ss.fffff"), channel.Id);
                return(new CoapResponse(message.MessageId, ResponseMessageType.Reset, ResponseCodeType.EmptyMessage));
            }

            CoapUri             uri = new CoapUri(message.ResourceUri.ToString());
            ResponseMessageType rmt = message.MessageType == CoapMessageType.Confirmable ? ResponseMessageType.Acknowledgement : ResponseMessageType.NonConfirmable;

            if (!await adapter.CanSubscribeAsync(uri.Resource, channel.IsEncrypted))
            {
                //not authorized
                Trace.TraceWarning("{0} - CoAP observe not authorized on channel '{1}'", DateTime.UtcNow.ToString("yyyy-MM-ddTHH-MM-ss.fffff"), channel.Id);
                return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Unauthorized, message.Token));
            }

            if (!message.Observe.Value)
            {
                //unsubscribe
                Trace.TraceWarning("{0} - CoAP observe with value on channel '{1}', unsubscribing.", DateTime.UtcNow.ToString("yyyy-MM-ddTHH-MM-ss.fffff"), channel.Id);
                await adapter.UnsubscribeAsync(uri.Resource);

                coapObserved.Remove(uri.Resource);
            }
            else
            {
                //subscribe
                SubscriptionMetadata metadata = new SubscriptionMetadata()
                {
                    IsEphemeral = true,
                    Identity    = session.Identity,
                    Indexes     = session.Indexes
                };

                string subscriptionUriString = await adapter.SubscribeAsync(uri.Resource, metadata);


                if (!coapObserved.ContainsKey(uri.Resource)) //add resource to observed list
                {
                    coapObserved.Add(uri.Resource, message.Token);
                }
            }

            return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Valid, message.Token));
        }
        private List <KeyValuePair <string, string> > GetIndexes(CoapUri coapUri)
        {
            List <KeyValuePair <string, string> > list = new List <KeyValuePair <string, string> >(coapUri.Indexes);

            if (coapUri.Indexes.Contains(new KeyValuePair <string, string>("~", "~")))
            {
                list.Remove(new KeyValuePair <string, string>("~", "~"));
                var query = config.GetClientIndexes().Where((ck) => ck.Key == session.Config.IdentityClaimType);
                if (query.Count() == 1)
                {
                    query.GetEnumerator().MoveNext();
                    list.Add(new KeyValuePair <string, string>(query.GetEnumerator().Current.Value, "~" + session.Identity));
                }
            }

            return(list.Count > 0 ? list : null);
        }
Пример #13
0
        private void Channel_OnOpen(object sender, ChannelOpenEventArgs e)
        {
            session.IsAuthenticated = Channel.IsAuthenticated;

            try
            {
                if (!Channel.IsAuthenticated && e.Message != null)
                {
                    CoapMessage msg     = CoapMessage.DecodeMessage(e.Message);
                    CoapUri     coapUri = new CoapUri(msg.ResourceUri.ToString());
                    session.IsAuthenticated = session.Authenticate(coapUri.TokenType, coapUri.SecurityToken);
                }

                if (session.IsAuthenticated)
                {
                    IdentityDecoder decoder = new IdentityDecoder(session.Config.IdentityClaimType, context, session.Config.Indexes);
                    session.Identity = decoder.Id;
                    session.Indexes  = decoder.Indexes;

                    UserAuditRecord record = new UserAuditRecord(Channel.Id, session.Identity, session.Config.IdentityClaimType, Channel.TypeId, "COAP", "Granted", DateTime.UtcNow);
                    userAuditor?.WriteAuditRecordAsync(record).Ignore();
                }
            }
            catch (Exception ex)
            {
                logger?.LogError(ex, $"CoAP adapter opening channel '{Channel.Id}'.");
                OnError?.Invoke(this, new ProtocolAdapterErrorEventArgs(Channel.Id, ex));
            }

            if (!session.IsAuthenticated && e.Message != null)
            {
                //close the channel
                logger?.LogInformation($"CoAP adapter user not authenticated; must close channel '{Channel.Id}'.");
                Channel.CloseAsync().Ignore();
            }
            else
            {
                dispatcher = new CoapRequestDispatcher(session, Channel);
            }
        }
        public async Task <CoapMessage> DeleteAsync(CoapMessage message)
        {
            Exception error = null;

            CoapUri uri = new CoapUri(message.ResourceUri.ToString());

            try
            {
                await adapter.UnsubscribeAsync(uri.Resource);

                await logger?.LogDebugAsync($"CoAP delete unsubscribe '{uri.Resource}' for {session.Identity}.");

                coapObserved.Remove(uri.Resource);
                await logger?.LogDebugAsync($"CoAP delete removed '{uri.Resource}' for {session.Identity}.");
            }
            catch (Exception ex)
            {
                await logger?.LogErrorAsync(ex, $"CoAP delete fault during unsubscribe process for {session.Identity}");

                error = ex;
            }

            if (error == null)
            {
                ResponseMessageType rmt = message.MessageType == CoapMessageType.Confirmable ? ResponseMessageType.Acknowledgement : ResponseMessageType.NonConfirmable;
                await logger?.LogDebugAsync($"CoAP delete returning response for '{uri.Resource}' with {rmt.ToString()} for {session.Identity}.");

                return(new CoapResponse(message.MessageId, rmt, ResponseCodeType.Deleted, message.Token));
            }
            else
            {
                await logger?.LogDebugAsync($"CoAP delete returning response for '{uri.Resource}' with {ResponseCodeType.EmptyMessage.ToString()} for {session.Identity}.");

                return(new CoapResponse(message.MessageId, ResponseMessageType.Reset, ResponseCodeType.EmptyMessage));
            }
        }
Пример #15
0
        public static byte[] ConvertToMqtt(MqttSession session, EventMessage message)
        {
            if (message.Protocol == ProtocolType.MQTT)
            {
                return(MqttConversion(session, message.Message));
            }
            else if (message.Protocol == ProtocolType.COAP)
            {
                CoapMessage msg  = CoapMessage.DecodeMessage(message.Message);
                CoapUri     curi = new CoapUri(msg.ResourceUri.ToString());
                QualityOfServiceLevelType qos = QualityOfServiceLevelType.AtLeastOnce;

                try
                {
                    QualityOfServiceLevelType?qosType = session.GetQoS(curi.Resource);
                    qos = qosType.HasValue ? qosType.Value : QualityOfServiceLevelType.AtLeastOnce;
                }
                catch (Exception ex)
                {
                    Trace.TraceWarning("{0} - Fault in ProtocolTransition.ConvertToMqtt", DateTime.UtcNow.ToString());
                    Trace.TraceError("{0} - {1} - {2}", DateTime.UtcNow.ToString(""), "ProtocolTransition", ex.Message);
                }

                PublishMessage pub = new PublishMessage(false, qos, false, session.NewId(), curi.Resource, msg.Payload);
                return(pub.Encode());
            }
            else if (message.Protocol == ProtocolType.REST)
            {
                PublishMessage pubm = new PublishMessage(false, session.GetQoS(message.ResourceUri).Value, false, session.NewId(), message.ResourceUri, message.Message);
                return(pubm.Encode());
            }
            else
            {
                return(MqttConversion(session, message.Message, message.ContentType));
            }
        }
Пример #16
0
        public static string ToCoreLinkFormat(CoapResourceMetadata resource, Uri baseUri = null)
        {
            var message = new StringBuilder();

            try
            {
                if (baseUri == null)
                {
                    baseUri = _throwAwayUri;
                }

                var uri = resource.UriReference.IsAbsoluteUri
                    ? resource.UriReference
                    : new Uri(baseUri, resource.UriReference);

                message.Append(CoapUri.Compare(uri, baseUri, UriComponents.HostAndPort, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) == 0
                    ? $"<{uri.AbsolutePath}>"
                    : $"<{uri}>");

                if (resource.InterfaceDescription.Count > 0)
                {
                    message.AppendFormat(";if=\"{0}\"", string.Join(" ", resource.InterfaceDescription));
                }

                if (resource.SuggestedContentTypes.Count == 1)
                {
                    message.Append($";ct={(int)resource.SuggestedContentTypes.First():D}");
                }

                if (resource.SuggestedContentTypes.Count > 1)
                {
                    message.AppendFormat(";ct=\"{0}\"", string.Join(" ", resource.SuggestedContentTypes.Select(ct => ((int)ct).ToString("D"))));
                }

                if (resource.ResourceTypes.Count > 0)
                {
                    message.AppendFormat(";rt=\"{0}\"", string.Join(" ", resource.ResourceTypes));
                }

                if (resource.Rel.Count == 1)
                {
                    message.AppendFormat(";rel={0}", resource.Rel.First());
                }
                if (resource.Rel.Count > 1)
                {
                    message.AppendFormat(";rel=\"{0}\"", string.Join(" ", resource.Rel));
                }

                if (!string.IsNullOrEmpty(resource.Anchor))
                {
                    message.Append($";anchor=\"{resource.Anchor}\"");
                }

                if ((resource.HrefLang ?? string.Empty) != string.Empty)
                {
                    message.Append($";hreflang={resource.HrefLang?.ToLower()}");
                }

                if ((resource.Media ?? string.Empty) != string.Empty)
                {
                    message.Append(resource.Media.Any(c => c == ';' || c == ',')
                        ? $";media=\"{resource.Media}\""
                        : $";media={resource.Media}");
                }

                if ((resource.Title ?? string.Empty) != string.Empty)
                {
                    message.Append($";title=\"{resource.Title}\"");
                }

                //case "title*":
                //    // TODO: No idea what to do here...?
                //    var charset = value.Substring(0, value.IndexOf('\''));
                //    var lang = value.Substring(charset.Length + 1,
                //        value.IndexOf('\'', charset.Length + 1) - charset.Length - 1);
                //    value = value.Substring(charset.Length + lang.Length + 3,
                //        value.Length - charset.Length - lang.Length - 4);

                //    //System.Diagnostics.Debug.WriteLine("title* = {3}\n\tCharset: {0}\n\tLanguage: {1}\n\tValue: {2}",
                //    //    charset, lang, value, Uri.UnescapeDataString(value));

                //    resource.TitleExt = Uri.UnescapeDataString(value);
                //    break;

                if ((resource.Type ?? string.Empty) != string.Empty)
                {
                    message.Append(resource.Type.Contains(" ")
                        ? $";type=\"{resource.Type}\""
                        : $";type={resource.Type}");
                }

                if (resource.MaxSize != 0)
                {
                    message.Append($";sz={resource.MaxSize:D}");
                }


                //default:
                //    if (value.Length == 1)
                //    {
                //        if (!char.IsLetterOrDigit(value[0]) && !new[]
                //        {
                //            '!', '#', '$', '%', '&', '\'', '(', ')', '*', '+', '-', '.', '/', ':',
                //            '<', '=', '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~'
                //        }.Contains(value[0]))
                //            throw new ArgumentException(
                //                $"PToken contains invalid character '{value[0]}' at pos {mSeek}");
                //        resource.Extentions.Add(param, value);
                //    }
                //    else
                //    {
                //        if (value[0] != '"')
                //            throw new ArgumentException(
                //                $"Expected QuotedString DQUOTE '\"' at pos {mPos}");
                //        if (value[value.Length - 1] != '"')
                //            throw new ArgumentException(
                //                $"Expected QuotedString DQUOTE '\"' at pos {mSeek}");
                //        resource.Extentions.Add(param,
                //            value.Substring(1, value.Length - 2));
                //    }
                //    break;
            }
            catch (IndexOutOfRangeException)
            {
                // Moving on...
            }
            return(message.ToString());
        }