/// <summary> /// Gets the http headers from a list of CoAP options. The method iterates /// over the list looking for a translation of each option in the predefined /// mapping. This process ignores the proxy-uri and the content-type because /// they are managed differently. If a mapping is present, the content of the /// option is mapped to a string accordingly to its original format and set /// as the content of the header. /// </summary> /// <param name="optionList"></param> /// <returns></returns> /// <exception cref="ArgumentNullException"></exception> public static NameValueCollection GetHttpHeaders(IList <Option> optionList) { if (optionList == null) { ThrowHelper.ArgumentNullException("optionList"); } NameValueCollection headers = new NameValueCollection(optionList.Count); foreach (Option opt in optionList) { // skip content-type because it should be translated while handling // the payload; skip proxy-uri because it has to be translated in a // different way if (opt.Type == OptionType.ContentType || opt.Type == OptionType.ProxyUri) { continue; } String headerName; if (!coap2httpHeader.TryGetValue(opt.Type, out headerName)) { continue; } // format the value String headerValue = null; OptionFormat format = Option.GetFormatByType(opt.Type); if (format == OptionFormat.Integer) { headerValue = opt.IntValue.ToString(); } else if (format == OptionFormat.String) { headerValue = opt.StringValue; } else if (format == OptionFormat.Opaque) { headerValue = ByteArrayUtils.ToHexStream(opt.RawValue); } else { continue; } // custom handling for max-age // format: cache-control: max-age=60 if (opt.Type == OptionType.MaxAge) { headerValue = "max-age=" + headerValue; } headers[headerName] = headerValue; } return(headers); }
/// <summary> /// Stringify options in a message. /// </summary> public static String OptionsToString(Message msg) { Boolean first = true; Action <StringBuilder, String, String> appendIfNotNullOrEmpty = delegate(StringBuilder builder, String header, String value) { if (String.IsNullOrEmpty(value)) { return; } if (first) { first = false; } else { builder.Append(", "); } builder.Append(header).Append("=").Append(value); }; StringBuilder sb = new StringBuilder(); appendIfNotNullOrEmpty(sb, "If-Match", ToString(msg.IfMatches, bs => ByteArrayUtils.ToHexString(bs))); if (msg.HasOption(OptionType.UriHost)) { appendIfNotNullOrEmpty(sb, "URI-Host", msg.UriHost); } appendIfNotNullOrEmpty(sb, "ETag", ToString(msg.ETags, bs => ByteArrayUtils.ToHexString(bs))); if (msg.IfNoneMatch) { appendIfNotNullOrEmpty(sb, "If-None-Match", msg.IfNoneMatch.ToString()); } if (msg.UriPort > 0) { appendIfNotNullOrEmpty(sb, "URI-Port", msg.UriPort.ToString()); } appendIfNotNullOrEmpty(sb, "Location-Path", ToString(msg.LocationPaths)); appendIfNotNullOrEmpty(sb, "URI-Path", ToString(msg.UriPaths)); if (msg.ContentType != MediaType.Undefined) { appendIfNotNullOrEmpty(sb, "Content-Type", MediaType.ToString(msg.ContentType)); } if (msg.HasOption(OptionType.MaxAge)) { appendIfNotNullOrEmpty(sb, "Max-Age", msg.MaxAge.ToString()); } appendIfNotNullOrEmpty(sb, "URI-Query", ToString(msg.UriQueries)); if (msg.Accept != MediaType.Undefined) { appendIfNotNullOrEmpty(sb, "Accept", MediaType.ToString(msg.Accept)); } appendIfNotNullOrEmpty(sb, "Location-Query", ToString(msg.LocationQueries)); if (msg.HasOption(OptionType.ProxyUri)) { appendIfNotNullOrEmpty(sb, "Proxy-URI", msg.ProxyUri.ToString()); } appendIfNotNullOrEmpty(sb, "Proxy-Scheme", msg.ProxyScheme); if (msg.Block1 != null) { appendIfNotNullOrEmpty(sb, "Block1", msg.Block1.ToString()); } if (msg.Block2 != null) { appendIfNotNullOrEmpty(sb, "Block2", msg.Block2.ToString()); } if (msg.Observe.HasValue) { appendIfNotNullOrEmpty(sb, "Observe", msg.Observe.ToString()); } return(sb.ToString()); }
/// <summary> /// Gets the coap options starting from an array of http headers. The /// content-type is not handled by this method. The method iterates over an /// array of headers and for each of them tries to find a predefined mapping /// if the mapping does not exists it skips the header /// ignoring it. The method handles separately certain headers which are /// translated to options (such as accept or cache-control) whose content /// should be semantically checked or requires ad-hoc translation. Otherwise, /// the headers content is translated with the appropriate format required by /// the mapped option. /// </summary> /// <param name="headers"></param> /// <returns></returns> /// <exception cref="ArgumentNullException"></exception> public static IEnumerable <Option> GetCoapOptions(NameValueCollection headers) { if (headers == null) { ThrowHelper.ArgumentNullException("headers"); } List <Option> list = new List <Option>(); foreach (String key in headers.AllKeys) { OptionType ot; if (!http2coapOption.TryGetValue(key, out ot)) { continue; } // ignore the content-type because it will be handled within the payload if (ot == OptionType.ContentType) { continue; } String headerValue = headers[key].Trim(); if (ot == OptionType.Accept) { // if it contains the */* wildcard, no CoAP Accept is set if (!headerValue.Contains("*/*")) { // remove the part where the client express the weight of each // choice headerValue = headerValue.Split(';')[0].Trim(); // iterate for each content-type indicated foreach (String headerFragment in headerValue.Split(',')) { // translate the content-type IEnumerable <Int32> coapContentTypes; if (headerFragment.Contains("*")) { coapContentTypes = MediaType.ParseWildcard(headerFragment); } else { coapContentTypes = new Int32[] { MediaType.Parse(headerFragment) } }; // if is present a conversion for the content-type, then add // a new option foreach (int coapContentType in coapContentTypes) { if (coapContentType != MediaType.Undefined) { // create the option Option option = Option.Create(ot); option.IntValue = coapContentType; list.Add(option); } } } } } else if (ot == OptionType.MaxAge) { int maxAge = 0; if (!headerValue.Contains("no-cache")) { headerValue = headerValue.Split(',')[0]; if (headerValue != null) { Int32 index = headerValue.IndexOf('='); if (!Int32.TryParse(headerValue.Substring(index + 1).Trim(), out maxAge)) { continue; } } } // create the option Option option = Option.Create(ot); option.IntValue = maxAge; list.Add(option); } else { Option option = Option.Create(ot); switch (Option.GetFormatByType(ot)) { case OptionFormat.Integer: option.IntValue = Int32.Parse(headerValue); break; case OptionFormat.Opaque: option.RawValue = ByteArrayUtils.FromHexStream(headerValue); break; case OptionFormat.String: default: option.StringValue = headerValue; break; } list.Add(option); } } return(list); }