idnits 2.17.1 draft-ietf-core-href-02.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- == The page length should not exceed 58 lines per page, but there was 1 longer page, the longest (page 1) being 741 lines Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- No issues found here. Miscellaneous warnings: ---------------------------------------------------------------------------- == The copyright year in the IETF Trust and authors Copyright Line does not match the current year -- The document date (8 January 2020) is 1541 days in the past. Is this intentional? Checking references for intended status: Proposed Standard ---------------------------------------------------------------------------- (See RFCs 3967 and 4897 for information about using normative references to lower-maturity documents in RFCs) ** Obsolete normative reference: RFC 7049 (Obsoleted by RFC 8949) -- Obsolete informational reference (is this intentional?): RFC 7230 (Obsoleted by RFC 9110, RFC 9112) Summary: 1 error (**), 0 flaws (~~), 2 warnings (==), 2 comments (--). Run idnits with the --verbose option for more detailed information about the items above. -------------------------------------------------------------------------------- 2 CoRE Working Group K. Hartke 3 Internet-Draft Ericsson 4 Intended status: Standards Track 8 January 2020 5 Expires: 11 July 2020 7 Constrained Resource Identifiers 8 draft-ietf-core-href-02 10 Abstract 12 Constrained Resource Identifiers (CoRIs) are an alternate 13 serialization of Uniform Resource Identifiers (URIs) that encodes the 14 URI components in Concise Binary Object Representation (CBOR) instead 15 of a string of characters. This simplifies parsing, reference 16 resolution, and comparison of URIs in environments with severe 17 limitations on processing power, code size, and memory size. 19 Note to Readers 21 This note is to be removed before publishing as an RFC. 23 The issues list for this Internet-Draft can be found at 24 . 26 A reference implementation and a set of test vectors can be found at 27 . 29 Status of This Memo 31 This Internet-Draft is submitted in full conformance with the 32 provisions of BCP 78 and BCP 79. 34 Internet-Drafts are working documents of the Internet Engineering 35 Task Force (IETF). Note that other groups may also distribute 36 working documents as Internet-Drafts. The list of current Internet- 37 Drafts is at https://datatracker.ietf.org/drafts/current/. 39 Internet-Drafts are draft documents valid for a maximum of six months 40 and may be updated, replaced, or obsoleted by other documents at any 41 time. It is inappropriate to use Internet-Drafts as reference 42 material or to cite them other than as "work in progress." 44 This Internet-Draft will expire on 11 July 2020. 46 Copyright Notice 48 Copyright (c) 2020 IETF Trust and the persons identified as the 49 document authors. All rights reserved. 51 This document is subject to BCP 78 and the IETF Trust's Legal 52 Provisions Relating to IETF Documents (https://trustee.ietf.org/ 53 license-info) in effect on the date of publication of this document. 54 Please review these documents carefully, as they describe your rights 55 and restrictions with respect to this document. Code Components 56 extracted from this document must include Simplified BSD License text 57 as described in Section 4.e of the Trust Legal Provisions and are 58 provided without warranty as described in the Simplified BSD License. 60 Table of Contents 62 1. Introduction 63 1.1. Notational Conventions 64 2. Data Model 65 2.1. Options 66 2.2. Option Sequences 67 3. CBOR 68 4. Python 69 4.1. Reference Resolution 70 4.2. URI Recomposition 71 4.3. CoAP Encoding 72 5. Security Considerations 73 6. IANA Considerations 74 7. References 75 7.1. Normative References 76 7.2. Informative References 77 Appendix A. Change Log 78 Acknowledgements 79 Author's Address 81 1. Introduction 83 Uniform Resource Identifier (URI) references [RFC3986] are the 84 standard way to link to resources in hypertext formats such as HTML 85 [W3C.REC-html52-20171214] or the HTTP "Link" header field [RFC8288]. 86 A URI reference is either a URI or a relative reference that must be 87 resolved against a base URI. 89 URI references are strings of characters chosen from the repertoire 90 of US-ASCII characters. The individual components of a URI reference 91 are delimited by a number of reserved characters, which necessitates 92 the use of percent-encoding when these reserved characters are used 93 in a non-delimiting function. One component can also contain special 94 dot-segments that affect how the component is to be interpreted. The 95 resolution of URI references involves parsing the character string 96 into its components, combining those components with the components 97 of a base URI, merging path components, removing dot-segments, and 98 recomposing the result back into a character string. 100 Overall, the proper processing of URIs is quite complicated. This 101 can be a problem in particular in constrained environments [RFC7228], 102 where devices often have severe code size limitations. As a result, 103 many implementations in these environments choose to support only an 104 ad-hoc, informally-specified, bug-ridden, non-interoperable subset of 105 half of the URI standard. 107 This document introduces Constrained Resource Identifier (CoRI) 108 references, an alternate serialization of URI references that encodes 109 the URI components in Concise Binary Object Representation (CBOR) 110 [RFC7049] instead of a string of characters. Assuming an 111 implementation of CBOR is already present on a device, typical 112 operations on URI references such as parsing, reference resolution, 113 and comparison can be implemented more easily than for character 114 strings. A full implementation that covers all corner cases is 115 intended to be implementable in a relatively small amount of code. 117 As a result of the simplification, CoRI references are not capable of 118 expressing all URI references permitted by the syntax of RFC 3986. 119 (Hence the "constrained" in "Constrained Resource Identifiers".) The 120 supported subset includes all Constrained Application Protocol (CoAP) 121 URIs [RFC7252], most Hypertext Transfer Protocol (HTTP) URIs 122 [RFC7230], and many other URIs that function as resource locators. 124 1.1. Notational Conventions 126 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 127 "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 128 "OPTIONAL" in this document are to be interpreted as described in BCP 129 14 [RFC2119] [RFC8174] when, and only when, they appear in all 130 capitals, as shown here. 132 Terms defined in this document appear in _cursive_ where they are 133 introduced. 135 2. Data Model 137 The data model for CoRI references is very similar to the 138 serialization of the request URI in CoAP messages [RFC7252]: The 139 components of a URI reference are encoded as a sequence of _options_, 140 where each path segment and query parameter becomes its own option. 141 Every option consists of an _option number_ identifying the type of 142 option (scheme, host name, path segment, etc.) and an _option value_. 144 2.1. Options 146 The following types of options are defined: 148 scheme 149 Specifies the URI scheme. The option value can be any Unicode 150 string matching the "scheme" rule described in Section 3.1 of RFC 151 3986 [RFC3986], excluding uppercase letters. 153 host.name 154 Specifies the host of the URI authority as a registered name. The 155 option value can be any Unicode string matching the specifications 156 of the URI scheme. 158 host.ip 159 Specifies the host of the URI authority as an IPv4 address or an 160 IPv6 address. The option value is a byte string with a length of 161 either 4 or 16 bytes, respectively. 163 port 164 Specifies the port number of the URI authority. The option value 165 is an integer in the range from 0 to 65535. 167 path.type 168 Specifies the type of the URI path for reference resolution. The 169 option value is an integer in the range from 0 to 127, named as 170 follows: 172 0 absolute-path 173 1 append-relation 174 2 append-path 175 3 relative-path 176 4 relative-path-1up 177 5 relative-path-2up 178 6 relative-path-3up 179 7 relative-path-4up 180 ... 181 127 relative-path-124up 183 path 184 Specifies one segment of the URI path. The option value can be 185 any Unicode string with the exception of "." and "..". This 186 option can occur more than once. 188 query 189 Specifies one argument of the URI query. The option value can be 190 any Unicode string. This option can occur more than once. 192 fragment 193 Specifies the fragment identifier. The option value can be any 194 Unicode string. 196 No percent-encoding is performed in option values. 198 2.2. Option Sequences 200 _ host.name _ 201 ____ scheme __/ \___ port _ 202 \ \________/ \__ host.ip __/ / \ 203 \__________________________/ ________/ 204 \ / ________ _________ 205 \ / / \ / \ 206 \__________ path.type __\_\_ path _/__\_ query _/__ fragment __ 207 \___________/ \________/ \_________/ \__________/ 209 Figure 1: Structure of a Well-Formed Sequence of Options 211 A sequence of options is considered _well-formed_ if: 213 * the sequence of options is empty or starts with a "scheme", 214 "host.name", "host.ip", "port", "path.type", "path", "query", or 215 "fragment" option; 217 * any "scheme" option is followed by either a "host.name" or a 218 "host.ip" option; 220 * any "host.name" option is followed by a "port" option; 222 * any "host.ip" option is followed by a "port" option; 224 * any "port" option is followed by a "path", "query", or "fragment" 225 option or is at the end of the sequence; 227 * any "path.type" option is followed by a "path", "query", or 228 "fragment" option or is at the end of the sequence; 230 * any "path" option is followed by a "path", "query", or "fragment" 231 option or is at the end of the sequence; 233 * any "query" option is followed by a "query" or "fragment" option 234 or is at the end of the sequence; and 236 * any "fragment" option is at the end of the sequence. 238 A well-formed sequence of options is considered _absolute_ if the 239 sequence of options starts with a "scheme" option. 241 A well-formed sequence of options is considered _relative_ if the 242 sequence of options is empty or starts with an option other than a 243 "scheme" option. 245 An absolute sequence of options is considered _normalized_ if the 246 result of resolving the sequence of options against any base is equal 247 to the input. (It doesn't matter what base it is resolved against, 248 since it is already absolute.) 250 The following operations can be performed on a sequence of options: 252 resolve(href, base) 253 Resolves a well-formed sequence of options `href` against an 254 absolute sequence of options `base`. This operation MUST be 255 performed by applying any algorithm that is functionally 256 equivalent to the reference implementation in Section 4.1 of this 257 document. 259 relative(href, base) 260 Makes an absolute sequence of options `href` relative to an 261 absolute sequence of options `base`. This operation MUST be 262 performed by applying any algorithm that returns a sequence of 263 options such that `resolve(relative(h, b), b)` is equal to `h` 264 given the same `b`. 266 recompose(href) 267 Recomposes a URI from an absolute sequence of options `href`. This 268 operation MUST be performed by applying any algorithm that is 269 functionally equivalent to the reference implementation in 270 Section 4.2 of this document. 272 To reduce variability, it is RECOMMENDED to uppercase the letters 273 in the hexadecimal notation when percent-encoding octets [RFC3986] 274 and to follow the recommendations of Section 4 of RFC 5952 for the 275 text representation of IPv6 addresses [RFC5952]. 277 decompose(str) 278 Decomposes a URI `str` into a sequence of options. This operation 279 MUST be performed by applying any algorithm that returns a 280 sequence of options such that `recompose(decompose(x))` is 281 equivalent to `x`. 283 coap(href) 284 Constructs CoAP options from an absolute, normalized sequence of 285 options. This operation MUST be performed by recomposing the 286 sequence of options to a URI (as described above) and decomposing 287 the URI into CoAP options (as specified in Section 6.4 of RFC 288 7252). A concise implementation of this algorithm is illustrated 289 in Section 4.3 of this document. 291 3. CBOR 293 In Concise Binary Object Representation (CBOR) [RFC7049], a sequence 294 of options is encoded as an array that contains the option numbers 295 and option values in alternating order. 297 The structure can be described in the Concise Data Definition 298 Language (CDDL) [RFC8610] as follows: 300 CoRI = [?(scheme: 1, text .regexp "[a-z][a-z0-9+.-]*"), 301 ?(host.name: 2, text // 302 host.ip: 3, bytes .size 4 / bytes .size 16), 303 ?(port: 4, 0..65535), 304 ?(path.type: 5, 0..127), 305 *(path: 6, text), 306 *(query: 7, text), 307 ?(fragment: 8, text)] 309 Examples: 311 [1, "coap", 312 3, h'C6336401', 313 4, 5683, 314 6, ".well-known", 315 6, "core"] 317 [5, 0, 318 6, ".well-known", 319 6, "core", 320 7, "rt=temperature-c"] 322 4. Python 324 In Python, a sequence of options is encoded as a list of tuples, 325 where each tuple contains one option number and one option value. 327 The following Python 3.6 code illustrates how to check a sequence of 328 options for being well-formed, absolute, and relative. 330 331 import enum 333 class Option(enum.IntEnum): 334 _BEGIN = 0 335 SCHEME = 1 336 HOST_NAME = 2 337 HOST_IP = 3 338 PORT = 4 339 PATH_TYPE = 5 340 PATH = 6 341 QUERY = 7 342 FRAGMENT = 8 343 _END = 9 345 class PathType(enum.IntEnum): 346 ABSOLUTE_PATH = 0 347 APPEND_RELATION = 1 348 APPEND_PATH = 2 349 RELATIVE_PATH = 3 350 RELATIVE_PATH_1UP = 4 351 RELATIVE_PATH_2UP = 5 352 RELATIVE_PATH_3UP = 6 353 RELATIVE_PATH_4UP = 7 355 _TRANSITIONS = ([Option.SCHEME, Option.HOST_NAME, Option.HOST_IP, 356 Option.PORT, Option.PATH_TYPE, Option.PATH, Option.QUERY, 357 Option.FRAGMENT, Option._END], 358 [Option.HOST_NAME, Option.HOST_IP], 359 [Option.PORT], 360 [Option.PORT], 361 [Option.PATH, Option.QUERY, Option.FRAGMENT, Option._END], 362 [Option.PATH, Option.QUERY, Option.FRAGMENT, Option._END], 363 [Option.PATH, Option.QUERY, Option.FRAGMENT, Option._END], 364 [Option.QUERY, Option.FRAGMENT, Option._END], 365 [Option._END]) 367 def is_well_formed(href): 368 previous = Option._BEGIN 369 for option, _ in href: 370 if option not in _TRANSITIONS[previous]: 371 return False 372 previous = option 373 if Option._END not in _TRANSITIONS[previous]: 374 return False 375 return True 377 def is_absolute(href): 378 return is_well_formed(href) and \ 379 (len(href) != 0 and href[0][0] == Option.SCHEME) 381 def is_relative(href): 382 return is_well_formed(href) and \ 383 (len(href) == 0 or href[0][0] != Option.SCHEME) 384 386 Examples: 388 [(Option.SCHEME, 'coap'), 389 (Option.HOST_IP, b'\xC6\x33\x64\x01'), 390 (Option.PORT, 5683), 391 (Option.PATH, '.well-known'), 392 (Option.PATH, 'core')] 394 [(Option.PATH_TYPE, PathType.ABSOLUTE_PATH), 395 (Option.PATH, '.well-known'), 396 (Option.PATH, 'core'), 397 (Option.QUERY, 'rt=temperature-c')] 399 4.1. Reference Resolution 401 The following Python 3.6 code defines how to resolve a sequence of 402 options that might be relative to a given base. 404 405 def resolve(base, href, relation=0): 406 if not is_absolute(base) or not is_well_formed(href): 407 return None 408 result = [] 409 option = Option.FRAGMENT 410 if len(href) != 0: 411 option = href[0][0] 412 if option == Option.HOST_IP: 413 option = Option.HOST_NAME 414 elif option == Option.PATH_TYPE: 415 type = href[0][1] 416 href = href[1:] 417 elif option == Option.PATH: 418 type = PathType.RELATIVE_PATH 419 option = Option.PATH_TYPE 420 if option != Option.PATH_TYPE or type == PathType.ABSOLUTE_PATH: 421 _copy_until(base, result, option) 422 else: 423 _copy_until(base, result, Option.QUERY) 424 if type == PathType.APPEND_RELATION: 425 _append_and_normalize(result, Option.PATH, str(relation)) 426 while type > PathType.APPEND_PATH: 427 if len(result) == 0 or result[-1][0] != Option.PATH: 428 break 429 del result[-1] 430 type -= 1 431 _copy_until(href, result, Option._END) 432 _append_and_normalize(result, Option._END, None) 433 return result 435 def _copy_until(input, output, end): 436 for option, value in input: 437 if option >= end: 438 break 439 _append_and_normalize(output, option, value) 441 def _append_and_normalize(output, option, value): 442 if option > Option.PATH: 443 if len(output) >= 2 and \ 444 output[-1] == (Option.PATH, '') and ( 445 output[-2][0] < Option.PATH_TYPE or ( 446 output[-2][0] == Option.PATH_TYPE and 447 output[-2][1] == PathType.ABSOLUTE_PATH)): 448 del output[-1] 449 if option > Option.FRAGMENT: 450 return 451 output.append((option, value)) 452 454 4.2. URI Recomposition 456 The following Python 3.6 code defines how to recompose a URI from an 457 absolute sequence of options. 459 460 def recompose(href): 461 if not is_absolute(href): 462 return None 463 result = '' 464 no_path = True 465 first_query = True 466 for option, value in href: 467 if option == Option.SCHEME: 468 result += value + ':' 469 elif option == Option.HOST_NAME: 470 result += '//' + _encode_reg_name(value) 471 elif option == Option.HOST_IP: 472 result += '//' + _encode_ip_address(value) 473 elif option == Option.PORT: 474 result += ':' + _encode_port(value) 475 elif option == Option.PATH: 476 result += '/' + _encode_path_segment(value) 477 no_path = False 478 elif option == Option.QUERY: 479 if no_path: 480 result += '/' 481 no_path = False 482 result += '?' if first_query else '&' 483 result += _encode_query_argument(value) 484 first_query = False 485 elif option == Option.FRAGMENT: 486 if no_path: 487 result += '/' 488 no_path = False 489 result += '#' + _encode_fragment(value) 490 if no_path: 491 result += '/' 492 no_path = False 493 return result 495 def _encode_reg_name(s): 496 return ''.join(c if _is_reg_name_char(c) 497 else _encode_pct(c) for c in s) 499 def _encode_ip_address(b): 500 if len(b) == 4: 501 return '.'.join(str(c) for c in b) 502 elif len(b) == 16: 503 return '[' + ... + ']' # see RFC 5952 505 def _encode_port(p): 506 return str(p) 508 def _encode_path_segment(s): 509 return ''.join(c if _is_segment_char(c) 510 else _encode_pct(c) for c in s) 512 def _encode_query_argument(s): 513 return ''.join(c if _is_query_char(c) and c not in '&' 514 else _encode_pct(c) for c in s) 516 def _encode_fragment(s): 517 return ''.join(c if _is_fragment_char(c) 518 else _encode_pct(c) for c in s) 520 def _encode_pct(s): 521 return ''.join('%{0:0>2X}'.format(c) for c in s.encode('utf-8')) 523 def _is_reg_name_char(c): 524 return _is_unreserved(c) or _is_sub_delim(c) 526 def _is_segment_char(c): 527 return _is_pchar(c) 529 def _is_query_char(c): 530 return _is_pchar(c) or c in '/?' 532 def _is_fragment_char(c): 533 return _is_pchar(c) or c in '/?' 535 def _is_pchar(c): 536 return _is_unreserved(c) or _is_sub_delim(c) or c in ':@' 538 def _is_unreserved(c): 539 return _is_alpha(c) or _is_digit(c) or c in '-._~' 541 def _is_alpha(c): 542 return c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + \ 543 'abcdefghijklmnopqrstuvwxyz' 545 def _is_digit(c): 546 return c in '0123456789' 548 def _is_sub_delim(c): 549 return c in '!$&\'()*+,;=' 550 552 4.3. CoAP Encoding 554 The following Python 3.6 code illustrates how to construct CoAP 555 options from an absolute sequence of options. For simplicity, the 556 code does not omit CoAP options with their default value. 558 559 def coap(href, to_proxy=False): 560 if not is_absolute(href): 561 return None 562 result = b'' 563 previous = 0 564 for option, value in href: 565 if option == Option.SCHEME: 566 pass 567 elif option == Option.HOST_NAME: 568 opt = 3 # Uri-Host 569 val = value.encode('utf-8') 570 result += _encode_coap_option(opt - previous, val) 571 previous = opt 572 elif option == Option.HOST_IP: 573 opt = 3 # Uri-Host 574 if len(value) == 4: 575 val = '.'.join(str(c) for c in value).encode('utf-8') 576 elif len(value) == 16: 577 val = b'[' + ... + b']' # see RFC 5952 578 result += _encode_coap_option(opt - previous, val) 579 previous = opt 580 elif option == Option.PORT: 581 opt = 7 # Uri-Port 582 val = value.to_bytes((value.bit_length() + 7) // 8, 'big') 583 result += _encode_coap_option(opt - previous, val) 584 previous = opt 585 elif option == Option.PATH: 586 opt = 11 # Uri-Path 587 val = value.encode('utf-8') 588 result += _encode_coap_option(opt - previous, val) 589 previous = opt 590 elif option == Option.QUERY: 591 opt = 15 # Uri-Query 592 val = value.encode('utf-8') 593 result += _encode_coap_option(opt - previous, val) 594 previous = opt 595 elif option == Option.FRAGMENT: 596 pass 597 if to_proxy: 598 (option, value) = href[0] 599 opt = 39 # Proxy-Scheme 600 val = value.encode('utf-8') 601 result += _encode_coap_option(opt - previous, val) 602 previous = opt 603 return result 605 def _encode_coap_option(delta, value): 606 length = len(value) 607 delta_nibble = _encode_coap_option_nibble(delta) 608 length_nibble = _encode_coap_option_nibble(length) 609 result = bytes([delta_nibble << 4 | length_nibble]) 610 if delta_nibble == 13: 611 delta -= 13 612 result += bytes([delta]) 613 elif delta_nibble == 14: 614 delta -= 256 + 13 615 result += bytes([delta >> 8, delta & 255]) 616 if length_nibble == 13: 617 length -= 13 618 result += bytes([length]) 619 elif length_nibble == 14: 620 length -= 256 + 13 621 result += bytes([length >> 8, length & 255]) 622 result += value 623 return result 625 def _encode_coap_option_nibble(n): 626 if n < 13: 627 return n 628 elif n < 256 + 13: 629 return 13 630 elif n < 65536 + 256 + 13: 631 return 14 632 634 5. Security Considerations 636 Parsers must operate on input that is assumed to be untrusted. This 637 means that parsers MUST fail gracefully in the face of malicious 638 inputs. Additionally, parsers MUST be prepared to deal with resource 639 exhaustion (e.g., resulting from the allocation of big data items) or 640 exhaustion of the call stack (stack overflow). See Section 8 of RFC 641 7049 [RFC7049] for security considerations relating to CBOR. 643 The security considerations discussed in Section 7 of RFC 3986 644 [RFC3986] also apply to Constrained Resource Identifiers. 646 6. IANA Considerations 648 This document has no IANA actions. 650 7. References 652 7.1. Normative References 654 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 655 Requirement Levels", BCP 14, RFC 2119, 656 DOI 10.17487/RFC2119, March 1997, 657 . 659 [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform 660 Resource Identifier (URI): Generic Syntax", STD 66, 661 RFC 3986, DOI 10.17487/RFC3986, January 2005, 662 . 664 [RFC7049] Bormann, C. and P. Hoffman, "Concise Binary Object 665 Representation (CBOR)", RFC 7049, DOI 10.17487/RFC7049, 666 October 2013, . 668 [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 669 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, 670 May 2017, . 672 [RFC8610] Birkholz, H., Vigano, C., and C. Bormann, "Concise Data 673 Definition Language (CDDL): A Notational Convention to 674 Express Concise Binary Object Representation (CBOR) and 675 JSON Data Structures", RFC 8610, DOI 10.17487/RFC8610, 676 June 2019, . 678 7.2. Informative References 680 [RFC5952] Kawamura, S. and M. Kawashima, "A Recommendation for IPv6 681 Address Text Representation", RFC 5952, 682 DOI 10.17487/RFC5952, August 2010, 683 . 685 [RFC7228] Bormann, C., Ersue, M., and A. Keranen, "Terminology for 686 Constrained-Node Networks", RFC 7228, 687 DOI 10.17487/RFC7228, May 2014, 688 . 690 [RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer 691 Protocol (HTTP/1.1): Message Syntax and Routing", 692 RFC 7230, DOI 10.17487/RFC7230, June 2014, 693 . 695 [RFC7252] Shelby, Z., Hartke, K., and C. Bormann, "The Constrained 696 Application Protocol (CoAP)", RFC 7252, 697 DOI 10.17487/RFC7252, June 2014, 698 . 700 [RFC8288] Nottingham, M., "Web Linking", RFC 8288, 701 DOI 10.17487/RFC8288, October 2017, 702 . 704 [W3C.REC-html52-20171214] 705 Faulkner, S., Eicholz, A., Leithead, T., Danilo, A., and 706 S. Moon, "HTML 5.2", World Wide Web Consortium 707 Recommendation REC-html52-20171214, 14 December 2017, 708 . 710 Appendix A. Change Log 712 This section is to be removed before publishing as an RFC. 714 Changes from -01 to -02: 716 * Changed the syntax of schemes to exclude upper case characters. 718 * Minor editorial improvements. 720 Changes from -00 to -01: 722 * None. 724 Acknowledgements 726 Thanks to Christian Amsuess, Ari Keranen, Jim Schaad, and Dave Thaler 727 for helpful comments and discussions that have shaped the document. 729 Author's Address 731 Klaus Hartke 732 Ericsson 733 Torshamnsgatan 23 734 SE-16483 Stockholm 735 Sweden 737 Email: klaus.hartke@ericsson.com