<?xml version="1.0" encoding="utf-8"?>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.nl" -->
<rfc version="3" ipr="trust200902" submissionType="IETF" category="info" xml:lang="en" consensus="true" xmlns:xi="http://www.w3.org/2001/XInclude" docName="draft-dunglas-mercure-03">

<front>
<title>The Mercure Protocol</title><seriesInfo value="draft-dunglas-mercure-03" stream="IETF" status="informational" name="Internet-Draft"></seriesInfo>
<author initials="K." surname="Dunglas" fullname="Kévin Dunglas"><organization abbrev="Les-Tilleuls.coop">Les-Tilleuls.coop</organization><address><postal><street>5 rue Hegel</street>
<city>Lille</city>
<code>59000</code>
<country>France</country>
</postal><email>kevin@les-tilleuls.coop</email>
</address></author>
<date/>
<area>Internet</area>
<workgroup>Network Working Group</workgroup>

<abstract>
<t>Mercure is a protocol enabling the pushing of data updates to web browsers and other HTTP clients in
a fast, reliable and battery-efficient way. It is especially useful for publishing real-time updates
of resources served through web APIs to reactive web and mobile apps.</t>
</abstract>

</front>

<middle>

<section anchor="terminology"><name>Terminology</name>
<t>The keywords <bcp14>MUST</bcp14>, <bcp14>MUST NOT</bcp14>, <bcp14>REQUIRED</bcp14>, <bcp14>SHALL</bcp14>, <bcp14>SHALL NOT</bcp14>, <bcp14>SHOULD</bcp14>, <strong>SHOULD
NOT</strong>, <bcp14>RECOMMENDED</bcp14>, <bcp14>MAY</bcp14>, and <bcp14>OPTIONAL</bcp14>, when they appear in this document, are to be
interpreted as described in <xref target="RFC2119"></xref>.</t>

<ul>
<li><t>Topic: An HTTP <xref target="RFC7230"></xref> or HTTPS <xref target="RFC2818"></xref> topic URL. The unit to which one can subscribe
to changes.</t>
</li>
<li><t>Publisher: An owner of a topic. Notifies the hub when the topic feed has been updated. As in
almost all pubsub systems, the publisher is unaware of the subscribers, if any. Other pubsub
systems might call the publisher the &quot;source&quot;. Typically a website or a web API, but can also be
a web browser.</t>
</li>
<li><t>Subscriber: A client application that subscribes to real-time updates of topics. Typically a
Progressive Web App or a Mobile App, but can also be a server.</t>
</li>
<li><t>Target: A subscriber, or a group of subscribers. A publisher is able to securely dispatch
updates to specific targets. Using an HTTP <xref target="RFC7230"></xref> or HTTPS <xref target="RFC2818"></xref> URL to identify
targets is <bcp14>RECOMMENDED</bcp14>.</t>
</li>
<li><t>Hub: A server that handles subscription requests and distributes the content to subscribers
when the corresponding topics have been updated (a hub implementation is provided in this
repository). Any hub <bcp14>MAY</bcp14> implement its own policies on who can use it.</t>
</li>
</ul>
</section>

<section anchor="discovery"><name>Discovery</name>
<t>If the publisher is a server, it <bcp14>SHOULD</bcp14> advertise the URL of one or more hubs to the subscriber,
allowing it to receive live updates when topics are updated. If more than one hub URL is specified,
it is <bcp14>RECOMMENDED</bcp14> that the publisher notifies each hub, so the subscriber <bcp14>MAY</bcp14> subscribe to
one or more of them.</t>
<t>The publisher <bcp14>SHOULD</bcp14> include at least one Link Header <xref target="RFC5988"></xref> with <tt>rel=mercure</tt> (a hub link
header). The target URL of these links <bcp14>MUST</bcp14> be a hub implementing the Mercure protocol.</t>
<t>Note: this relation type has not been registered yet <xref target="RFC5988"></xref>. In the meantime, the relation type
<tt>https://git.io/mercure</tt> <bcp14>MAY</bcp14> be used instead.</t>
<t>The publisher <bcp14>MAY</bcp14> provide the following target attributes in the Link Headers:</t>

<ul>
<li><t><tt>last-event-id</tt>: the globally unique identifier of the last event dispatched by the publisher
at the time of the generation of this resource. If provided, it <bcp14>MUST</bcp14> be passed to the
hub through a query parameter called <tt>Last-Event-ID</tt> and will be used to ensure that possible
updates having been made during between the resource generation time and the connection to the
hub are not lost. See section #Re-Connection-and-State-Reconciliation. If this attribute is
provided, the publisher <bcp14>MUST</bcp14> always set the <tt>id</tt> parameter when sending updates to the hub.</t>
</li>
<li><t><tt>content-type</tt>: the content type of the updates that will pushed by the hub. If omitted, the
subscriber <bcp14>MUST</bcp14> assume that the content type will be the same as that of the original
resource. Setting the <tt>content-type</tt> attribute is especially useful to hint that partial updates
will be pushed, using formats such as JSON Patch <xref target="RFC6902"></xref> or JSON Merge Patch <xref target="RFC7386"></xref>.</t>
</li>
<li><t><tt>key-set=&lt;JWKS&gt;</tt>: the key(s) to decrypt updates encoded in the JWKS (JSON Web Key Set) format
(see the Encryption section).</t>
</li>
</ul>
<t>All these attributes are optional.</t>
<t>The publisher <bcp14>MAY</bcp14> also include one Link Header <xref target="RFC5988"></xref> with <tt>rel=self</tt> (the self link
header). It <bcp14>SHOULD</bcp14> contain the canonical URL for the topic to which subscribers are expected
to use for subscriptions. If the Link with <tt>rel=self</tt> is omitted, the current URL of the resource
<bcp14>MUST</bcp14> be used as a fallback.</t>
<t>Minimal example:</t>

<sourcecode type="http">GET /books/foo.jsonld HTTP/1.1
Host: example.com

HTTP/1.1 200 Ok
Content-type: application/ld+json
Link: &lt;https://example.com/hub&gt;; rel=&quot;mercure&quot;

{&quot;@id&quot;: &quot;/books/foo.jsonld&quot;, &quot;foo&quot;: &quot;bar&quot;}
</sourcecode>
<t>Links embedded in HTML or XML documents (as defined in the WebSub recommendation) <bcp14>MAY</bcp14> also be
supported by subscribers.</t>
<t>Note: the discovery mechanism described in this section <eref target="https://www.w3.org/TR/websub/#discovery">is strongly inspired from the one specified
in the WebSub recommendation</eref>.</t>
</section>

<section anchor="subscriptions"><name>Subscriptions</name>
<t>The subscriber subscribes to a URL exposed by a hub to receive updates from one or many topics.
To subscribe to updates, the client opens an HTTPS connection following the <eref target="https://html.spec.whatwg.org/multipage/server-sent-events.html">Server-Sent Events
specification</eref> to the hub's
subscription URL advertised by the publisher. The <tt>GET</tt> HTTP method must be used. The connection
<bcp14>SHOULD</bcp14> use HTTP/2 to leverage mutliplexing and other advanced features of this protocol.</t>
<t>The subscriber specifies the list of topics to get updates from by using one or several query
parameters named <tt>topic</tt>. The value of these query parameters <bcp14>MUST</bcp14> be URI templates <xref target="RFC6570"></xref>.</t>
<t>Note: a URL is also a valid URI template.</t>
<t>The protocol doesn't specify the maximum number of <tt>topic</tt> parameters that can be sent, but the hub
<bcp14>MAY</bcp14> apply an arbitrary limit.</t>
<t>The <eref target="https://html.spec.whatwg.org/multipage/server-sent-events.html#the-eventsource-interface">EventSource JavaScript
interface</eref>
<bcp14>MAY</bcp14> be used to establish the connection. Any other
appropriate mechanism including, but not limited to, <eref target="https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams">readable
streams</eref> and
<eref target="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest">XMLHttpRequest</eref>
(used by popular polyfills) <bcp14>MAY</bcp14> also be used.</t>
<t>The hub sends updates concerning all subscribed resources matching the provided URI templates
and the provided targets (see section #Authorization). If no targets are specified, the update is
dispatched to all subscribers. The hub <bcp14>MUST</bcp14> send these updates as <eref target="https://html.spec.whatwg.org/multipage/server-sent-events.html#sse-processing-model">text/event-stream compliant
events</eref>.</t>
<t>The <tt>data</tt> property <bcp14>MUST</bcp14> contain the new version of the topic. It can be the full resource, or a
partial update by using formats such as JSON Patch <tt>@RFC6902</tt> or JSON Merge Patch <tt>@RFC7386</tt>.</t>
<t>All other properties defined in the Server-Sent Events specification <bcp14>MAY</bcp14> be used and <bcp14>SHOULD</bcp14>
be supported by hubs.</t>
<t>The resource <bcp14>SHOULD</bcp14> be represented in a format with hypermedia capabilities such as
JSON-LD <xref target="W3C.REC-json-ld-20140116"></xref>, Atom <xref target="RFC4287"></xref>, XML <xref target="W3C.REC-xml-20081126"></xref> or HTML
<xref target="W3C.REC-html52-20171214"></xref>.</t>
<t>Web Linking <xref target="RFC5988"></xref> <bcp14>SHOULD</bcp14> be used to indicate the IRI of the resource sent in the event.
When using Atom, XML or HTML as the serialization format for the resource, the document <bcp14>SHOULD</bcp14>
contain a <tt>link</tt> element with a <tt>self</tt> relation containing the IRI of the resource. When using
JSON-LD, the document <bcp14>SHOULD</bcp14> contain an <tt>@id</tt> property containing the IRI of the resource.</t>
<t>Example:</t>

<sourcecode type="javascript">// The subscriber subscribes to updates for the https://example.com/foo topic
// and to any topic matching https://example.com/books/{name}
const url = new URL('https://example.com/hub');
url.searchParams.append('topic', 'https://example.com/foo');
url.searchParams.append('topic', 'https://example.com/bar/{id}');

const eventSource = new EventSource(url);

// The callback will be called every time an update is published
eventSource.onmessage = function ({data}) {
    console.log(data);
};
</sourcecode>
<t>The hub <bcp14>MAY</bcp14> require that subscribers are authorized to receive updates.</t>
</section>

<section anchor="publication"><name>Publication</name>
<t>The publisher send updates by issuing <tt>POST</tt> HTTPS requests on the hub URL. When it receives an
update, the hub dispatches it to subscribers using the established server-sent events connections.</t>
<t>An application CAN send events directly to subscribers without using an external hub server, if it
is able to do so. In this case, it <strong>MAY NOT</strong> implement the endpoint to publish updates.</t>
<t>The request <bcp14>MUST</bcp14> be encoded using the <tt>application/x-www-form-urlencoded</tt> format and contain the
following data:</t>

<ul>
<li><t><tt>topic</tt>: IRIs of the updated topic. If this key is present several times, the first occurrence
is considered to be the canonical URL of the topic, and other ones are considered to be
alternate URLs. The hub <bcp14>MUST</bcp14> dispatch this update to subscribers that are subscribed to both
canonical or alternate URLs.</t>
</li>
<li><t><tt>data</tt>: The content of the new version of this topic.</t>
</li>
<li><t><tt>target</tt> (optional): Target audience of this update. This key can be present several times. See
section #Authorization for further information.</t>
</li>
<li><t><tt>id</tt> (optional): The topic's revision identifier: it will be used as the SSE's <tt>id</tt> property.
If omitted, the hub <bcp14>MUST</bcp14> generate a valid globally unique id. It <bcp14>MAY</bcp14> be a UUID. Even if
provided, the hub <bcp14>MAY</bcp14> ignore the id provided by the client and generate its own id.</t>
</li>
<li><t><tt>type</tt> (optional): The SSE's <tt>event</tt> property (a specific event type).</t>
</li>
<li><t><tt>retry</tt> (optional): The SSE's <tt>retry</tt> property (the reconnection time).</t>
</li>
</ul>
<t>In the event of success, the HTTP response's body <bcp14>MUST</bcp14> be the <tt>id</tt> associated to this update
generated by the hub and a success HTTP status code <bcp14>MUST</bcp14> be returned. The publisher <bcp14>MUST</bcp14> be
authorized to publish updates. See section #Authorization.</t>
</section>

<section anchor="authorization"><name>Authorization</name>
<t>To ensure that they are authorized, both publishers and subscribers must present a valid JWS
<xref target="RFC7515"></xref> in compact serialization to the hub. This JWS <bcp14>SHOULD</bcp14> be short-lived, especially
if the subscriber is a web browser. A different key <bcp14>MAY</bcp14> be used to sign subscribers' and
publishers' tokens.</t>
<t>Two mechanisms are defined to present the JWS to the hub:</t>

<ul>
<li><t>using an <tt>Authorization</tt> HTTP header</t>
</li>
<li><t>using a cookie</t>
</li>
</ul>
<t>If the publisher or the subscriber is not a web browser, it <bcp14>SHOULD</bcp14> use an <tt>Authorization</tt>
HTTP header. This <tt>Authorization</tt> header <bcp14>MUST</bcp14> contain the string <tt>Bearer</tt> followed by the JWS.
The hub will check that the JWS conforms to the rules (defined later) ensuring that the client is
authorized to publish or subscribe to updates.</t>
<t>By the <tt>EventSource</tt> specification, web browsers can not set custom HTTP headers for such
connections, and they can only be estabilished using the <tt>GET</tt> HTTP method. However, cookies
are supported and can be included even in cross-domain requests if <eref target="https://html.spec.whatwg.org/multipage/server-sent-events.html#dom-eventsourceinit-withcredentials">the CORS credentials are
set</eref>:</t>
<t>If the publisher or the subscriber is a web browser, it <bcp14>SHOULD</bcp14> send a cookie called
<tt>mercureAuthorization</tt> containing the JWS when connecting to the hub.</t>
<t>Whenever possible, the <tt>mercureAuthorization</tt> cookie <bcp14>SHOULD</bcp14> be set during the discovery to
improve the overall security. See section #Discovery. Consequently, if the cookie is set during the
discovery, both the publisher and the hub have to share the same second level domain. The <tt>Domain</tt>
attribute <bcp14>MAY</bcp14> be used to allow the publisher and the hub to use different subdomains.</t>
<t>The cookie <bcp14>SHOULD</bcp14> have the <tt>Secure</tt>, <tt>HttpOnly</tt> and <tt>SameSite</tt> attributes set. The cookie's
<tt>Path</tt> attribute <bcp14>SHOULD</bcp14> also be set to the hub's URL. See section #Security-Considerations.</t>
<t>When using authorization mechanisms, the connection <bcp14>MUST</bcp14> use an encryption layer such as HTTPS.</t>
<t>If both an <tt>Authorization</tt> HTTP header and a cookie named <tt>mercureAuthorization</tt> are presented by
the client, the cookie <bcp14>MUST</bcp14> be ignored. If the client tries to execute an operation it is not
allowed to, a 403 HTTP status code <bcp14>SHOULD</bcp14> be returned.</t>

<section anchor="publishers"><name>Publishers</name>
<t>Publishers <bcp14>MUST</bcp14> be authorized to dispatch updates to the hub, and <bcp14>MUST</bcp14> prove that they are
allowed to send updates.</t>
<t>To be allowed to publish an update, the JWT presented by the publisher <bcp14>MUST</bcp14> contain a claim
called <tt>mercure</tt>, and this claim <bcp14>MUST</bcp14> contain a <tt>publish</tt> key. <tt>mercure.publish</tt> <bcp14>MUST</bcp14>
contain an array of targets the publisher is allowed to dispatch updates to.</t>
<t>If <tt>mercure.publish</tt>:</t>

<ul>
<li><t>is not defined, then the publisher <bcp14>MUST NOT</bcp14> be authorized to dispatch any update</t>
</li>
<li><t>contains an empty array, then the publisher is only allowed to dispatch public updates</t>
</li>
<li><t>contains the reserved string <tt>*</tt> as an array value, then the publisher is authorized to dispatch
updates to all targets</t>
</li>
</ul>
<t>If a topic is not public, the <tt>POST</tt> request sent by the publisher to the hub <bcp14>MUST</bcp14> contain a
list of keys named <tt>target</tt>. Their values <bcp14>MUST</bcp14> be of type <tt>string</tt>, and it is <bcp14>RECOMMENDED</bcp14> to
use valid IRIs. They can be, for instance, a user ID or a list of group IDs. If an update contains
at least one target the publisher is not authorized for, the hub <bcp14>MUST NOT</bcp14> dispatch the update
(even if some targets in the list are allowed) and <bcp14>SHOULD</bcp14> return a 403 HTTP status code.</t>
</section>

<section anchor="subscribers"><name>Subscribers</name>
<t>Subscribers <bcp14>MAY</bcp14> need to be authorized to connect to the hub. To receive updates destined to
specific targets, they <bcp14>MUST</bcp14> be authorized, and <bcp14>MUST</bcp14> prove they belong to at least one of the
specified targets. If the subscriber is not authorized, it <bcp14>MUST NOT</bcp14> receive any update having at
least one target.</t>
<t>To receive updates destined for specific targets, the JWS presented by the subscriber <bcp14>MUST</bcp14> have
a claim named <tt>mercure</tt> with a key named <tt>subscribe</tt> that contains an array of strings: a list of
targets the user is authorized to receive updates for. The targets <bcp14>SHOULD</bcp14> be IRIs.</t>
<t>If at least one target is specified, the update <bcp14>MUST NOT</bcp14> be sent to the subscriber by the hub,
unless the <tt>mercure.subscribe</tt> array of the JWS presented by the subscriber contains at least one of
the specified targets.</t>
<t>If the <tt>mercure.subscribe</tt> array contains the reserved string value <tt>*</tt>, then the subscriber is
authorized to receive updates destined for all targets.</t>
</section>
</section>

<section anchor="reconnection-and-state-reconciliation"><name>Reconnection and State Reconciliation</name>
<t>To allow re-establishment in case of connection lost, events dispatched by the hub <bcp14>SHOULD</bcp14>
include an <tt>id</tt> property. The value contained in this <tt>id</tt> property <bcp14>SHOULD</bcp14> be a globally unique
identifier. To do so, a UUID <xref target="RFC4122"></xref> <bcp14>MAY</bcp14> be used.</t>
<t>According to the server-sent events specification, in case of connection
lost the subscriber will try to automatically re-connect. During the
re-connection, the subscriber <bcp14>MUST</bcp14> send the last received event id in a
<eref target="https://html.spec.whatwg.org/multipage/iana.html#last-event-id">Last-Event-ID</eref> HTTP header.</t>
<t>The server-sent events specification doesn't allow this HTTP header to be set during the first
connection (before a reconnection). In order to fetch any update dispatched between the initial
resource generation by the publisher and the connection to the hub, the subscriber <bcp14>MUST</bcp14> send the
event id provided during the discovery in the <tt>last-event-id</tt> link's attribute in a query parameter
named <tt>Last-Event-ID</tt> when connecting to the hub.</t>
<t>If both the <tt>Last-Event-ID</tt> HTTP header and the query parameter are present, the HTTP header
<bcp14>MUST</bcp14> take precedence.</t>
<t>If the <tt>Last-Event-ID</tt> HTTP header or query parameter exists, the hub <bcp14>SHOULD</bcp14> send all events
published following the one bearing this identifier to the subscriber.</t>
<t>The hub <bcp14>MAY</bcp14> discard some messages for operational reasons. The subscriber <bcp14>MUST NOT</bcp14> assume
that no update will be lost, and <bcp14>MUST</bcp14> re-fetch the original topic to ensure this (for instance,
after a long disconnection time).</t>
<t>The hub <bcp14>MAY</bcp14> also specify the reconnection time using the <tt>retry</tt> key, as specified in the
server-sent events format.</t>
</section>

<section anchor="encryption"><name>Encryption</name>
<t>Using HTTPS does not prevent the hub from accessing the update's content. Depending of the intended
privacy of information contained in the update, it <bcp14>MAY</bcp14> be necessary to prevent eavesdropping by
the hub.</t>
<t>To make sure that the message content can not be read by the hub, the publisher <bcp14>MAY</bcp14> encode the
message before sending it to the hub. The publisher <bcp14>SHOULD</bcp14> use JSON Web Encryption <xref target="RFC7516"></xref>
to encrypt the update content. The publisher <bcp14>MAY</bcp14> provide the relevant encryption key(s) in the
<tt>key-set</tt> attribute of the Link HTTP header during the discovery. The <tt>key-set</tt> attribute <bcp14>SHOULD</bcp14>
contain a key encoded using the JSON Web Key Set <xref target="RFC7517"></xref> format. Any other out-of-band mechanism
<bcp14>MAY</bcp14> be used instead to share the key between the publisher and the subscriber.</t>
<t>Update encyption is considered a best practice to prevent mass surveillance. This is especially
relevant if the hub is managed by an external provider.</t>
</section>

<section anchor="security-considerations"><name>Security Considerations</name>
<t>The confidentiality of the secret key(s) used to generate the JWTs is a primary concern. The
secret key(s) <bcp14>MUST</bcp14> be stored securely. They <bcp14>MUST</bcp14> be revoked immediately in the event of
compromission.</t>
<t>Possessing valid JWTs allows any client to subscribe, or to publish to the hub. Their
confidentiality <bcp14>MUST</bcp14> therefore be ensured. To do so, JWTs <bcp14>MUST</bcp14> only be transmitted over
secure connections.</t>
<t>Also, when the client is a web browser, the JWT <bcp14>SHOULD</bcp14> not be made accessible
to JavaScript scripts for resilience against <eref target="https://www.owasp.org/index.php/Cross-site_Scripting_(XSS">Cross-site Scription (XSS)
attacks</eref>). It's the main reason why,
when the client is a web browser, using <tt>HttpOnly</tt> cookies as the authorization mechanism <bcp14>SHOULD</bcp14>
always be preferred.</t>
<t>In the event of compromission, revoking JWTs before their expiration is often difficult. To that
end, using short-lived tokens is strongly <bcp14>RECOMMENDED</bcp14>.</t>
<t>The publish endpoint of the hub may be targeted by <eref target="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF">Cross-Site Request Forgery (CSRF)
attacks</eref>) when the cookie-based
authorization mechanism is used. Therefore, implementations supporting this mechanism <bcp14>MUST</bcp14>
mitigate such attacks.</t>
<t>The first prevention method to implement is to set the <tt>mercureAuthorization</tt>
cookie's <tt>SameSite</tt> attribute. However, <eref target="https://caniuse.com/#feat=same-site-cookie-attribute">some web browsers still not support this
attribute</eref> and will remain vulnerable.
Additionally, hub implementations <bcp14>SHOULD</bcp14> use the <tt>Origin</tt> and <tt>Referer</tt> HTTP headers set by web
browsers to verify that the source origin matches the target origin. If none of these headers are
available, the hub <bcp14>SHOULD</bcp14> discard the request.</t>
<t>CSRF prevention techniques, including those previously mentioned, are described
in depth in <eref target="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF">OWASP's Cross-Site Request Forgery (CSRF) Prevention Cheat
Sheet</eref><em>Prevention</em>Cheat_Sheet).</t>
</section>

</middle>

<back>
<references><name>Normative References</name>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.5988.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7517.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.2818.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.6570.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7515.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.4122.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7516.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7230.xml"/>
</references>
<references><name>Informative References</name>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml-w3c/reference.W3C.REC-json-ld-20140116.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml-w3c/reference.W3C.REC-xml-20081126.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7386.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.6902.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.4287.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml-w3c/reference.W3C.REC-html52-20171214.xml"/>
</references>

</back>

</rfc>
