Internet-Draft | ALTO/H2 | May 2022 |
Schott, et al. | Expires 24 November 2022 | [Page] |
The ALTO base protocol [RFC7285] uses HTTP/1.x as the transport protocol and hence ALTO transport includes the limitations of HTTP/1.x. ALTO/SSE [RFC8895] addresses some of the limitations, but is still based on HTTP/1.x. This document introduces ALTO new transport, which provides the transport functions of ALTO/SSE on top of HTTP/2, for more efficient ALTO transport.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119][RFC8174] when, and only when, they appear in all capitals, as shown here.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 24 November 2022.¶
Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Application-Layer Traffic Optimization (ALTO) provides a means for network applications to obtain network status information. The ALTO base protocol [RFC7285] is based on the sequential request and response model of HTTP/1.1 [RFC7230]; hence, in the base protocol, an ALTO client can issue only a sequence of requests on network information resources, and the ALTO server sends the information resources one-by-one, in the order of the request sequence.¶
To address the use cases where an ALTO client may need to efficiently monitor changes to a set of network information resources and the protocol is still based on the HTTP/1.1 model, the ALTO Working Group introduces ALTO/SSE (ALTO Incremental Update based on Server-Sent-Event) [RFC8895], so that an ALTO client can manage (i.e., add and remove) a set of requests maintained at an ALTO server, and the server can continuously, concurrently, and incrementally push updates whenever a monitored network information resource changes. Figure 1 shows the architecture and message flow of ALTO/SSE, which can be considered as a more general transport protocol than the ALTO base transport protocol. Although ALTO/SSE allows the concurrent transport of multiple ALTO information resources, it has complexities and limitations. For example, it requires that the server provide a separate control URI, leading to complexity in management.¶
This document specifies ALTO/H2, which realizes ALTO/SSE but takes advantage of new HTTP capabilities provided by HTTP/2 [RFC7540].¶
ALTO/H2 is designed to satisfy a set of requirements. First, it should satisfy the following requirements to realize the functions of ALTO/SSE:¶
Following the ALTO framework [RFC7285] [RFC7971], ALTO/H2 should still be HTTP based:¶
To allow flexible deployment, the new transport protocol should be flexible:¶
A key design of ALTO new transport is to distinguish between information about ALTO resources and information about ALTO transport. It introduces the following transport information structures to distribute ALTO information resources:¶
Figure 2 shows an example illustrating the aforementioned information. Each ALTO client (Client 1, Client 2, Client 3) maintains a single HTTP/2 connection with the ALTO server.¶
The basic work flow of a client connecting to an ALTO server is the following:¶
A transport queue supports three basic operations (CRD): create, read (get status), and delete.¶
Create a transport queue: An ALTO client creates a transport queue using the HTTP POST method with ALTO SSE AddUpdateReq ([RFC 8895] Sec. 6.5) as the parameter:¶
object { ResourceID resource-id; [JSONString tag;] [Boolean incremental-changes;] [Object input;] } AddUpdateReq;¶
A successful POST request MUST return the URI for the transport queue. Unless the request has incremental-changes to be false, the client is added to receiver set as well, indicating that the client will receive automatic, incremental push updates.¶
Read a transport queue: A client reads the status of a transport queue by issuing a GET request to the transport queue URI returned from the POST method.¶
Delete a transport queue: a transport queue exposed to a client can be closed (deleted) either explicitly or implicitly.¶
Error codes: ALTO/H2 uses HTTP error codes.¶
The first example is a client creating a transport queue.¶
Client -> server request HEADERS - END_STREAM + END_HEADERS :method = POST :scheme = https :path = /tqs host = alto.example.com accept = application/alto-error+json, application/alto-transport+json content-type = application/alto-transport+json content-length = TBD DATA - END_STREAM { "resource-id": "my-routingcost-map" }¶
Server -> client response: HEADERS - END_STREAM + END_HEADERS :status = 200 content-type = application/alto-transport+json content-length = TBD DATA - END_STREAM {"tq": “/tqs/2718281828459”}¶
The client can then read the status of the transport queue using the read operation (GET) in the same HTTP connection. Below is an example (structure of incremental updates queue will be specified in the next section):¶
Client -> server request HEADERS - END_STREAM + END_HEADERS :method = GET :scheme = https :path = /tqs/2718281828459 host = alto.example.com accept = application/alto-error+json, application/alto-transport+json Server -> client response: HEADERS - END_STREAM + END_HEADERS :status = 200 content-type = application/alto-transport+json content-length = TBD DATA - END_STREAM { "uq": [ {“seq”: 101, "media-type": "application/alto-costmap+json", “tag”: "a10ce8b059740b0b2e3f8eb1d4785acd42231bfe" }, {“seq”: 102, "media-type": "application/merge-patch+json", “tag”: "cdf0222x59740b0b2e3f8eb1d4785acd42231bfe" }, {“seq”: 103, "media-type": "application/merge-patch+json", “tag”: "8eb1d4785acd42231bfecdf0222x59740b0b2e3f", "link": "/tqs/2718281828459/snapshot/2e3f"} ], "rs": ["self"] }¶
Among the CRUD operations, an incremental updates queue supports only the read operation: a client cannot create, update, or delete incremental updates queue directly---it is read only, and associated with transport queue automatically.¶
Reads an incremental updates queue: A client reads the status of an incremental updates queue using the HTTP GET method: GET transport-queue-uri/uq, where the transport-queue-uri is the URI returned in the transport queue create method.¶
The response informs the client the backlog status, and potential direct links. Specifically, the response is a JSON array, with each element being one incremental update, with three required fields and one optional field:¶
Note that the server determines the state (window of history and type of each update) in the incremental updates queue, as specified by [R4].¶
Assume the same example in the preceding section. The client can check the status of the incremental updates queue of a transport queue from the same connection:¶
Client -> server request: HEADERS - END_STREAM + END_HEADERS :method = GET :scheme = https :path = /tqs/2718281828459/uq host = alto.example.com accept = application/alto-error+json, application/alto-transport+json Server -> client response: HEADERS - END_STREAM + END_HEADERS :status = 200 content-type = application/alto-transport+json content-length = TBD DATA - END_STREAM { [ {“seq”: 101, "media-type": "application/alto-costmap+json", “tag”: "a10ce8b059740b0b2e3f8eb1d4785acd42231bfe" }, {“seq”: 102, "media-type": "application/merge-patch+json", “tag”: "cdf0222x59740b0b2e3f8eb1d4785acd42231bfe" }, {“seq”: 103, "media-type": "application/merge-patch+json", “tag”: "8eb1d4785acd42231bfecdf0222x59740b0b2e3f", "link": "/tqs/2718281828459/snapshot/2e3f"} ], }¶
A client can only read an individual update. The read can be either pull read issued by the client or a push from the server to the client.¶
Client pull read: A client uses HTTP GET method on the incremental updates queue concatenated by the "seq" to pull an individual update.¶
Server push read: a client starts to receive server push when it is added to the receiver set. A client can add itself to the receiver set when creating the transport queue, or add itself explicitly to the receiver set (see the next section).¶
The work flow of server push of individual updates is the following:¶
The first example is a client pull example, in which the client directly requests an individual update.¶
Client -> server request: HEADERS + END_STREAM + END_HEADERS :method = GET :scheme = https :path = /tqs/2718281828459/uq/101 host = alto.example.com accept = application/alto-error+json, application/alto-costmap+json Server -> client response: HEADERS - END_STREAM + END_HEADERS :status = 200 content-type = application/alto-costmap+json content-length = TBD DATA + END_STREAM { "meta" : { "dependent-vtags" : [{ "resource-id": "my-network-map", "tag": "da65eca2eb7a10ce8b059740b0b2e3f8eb1d4785" }], "cost-type" : { "cost-mode" : "numerical", "cost-metric": "routingcost" }, "vtag": { "resource-id" : "my-routingcost-map", "tag" : "3ee2cb7e8d63d9fab71b9b34cbf764436315542e" } }, "cost-map" : { "PID1": { "PID1": 1, "PID2": 5, "PID3": 10 }, "PID2": { "PID1": 5, "PID2": 1, "PID3": 15 }, "PID3": { "PID1": 20, "PID2": 15 } } }¶
Note from the transport queue state that the 103 message has an OPTIONAL link to a complete snapshot, which a client can request.¶
Instead of directly requesting, the client can wait for the server for incremental push, where the server first sends PUSH_PROMISE with the GET URI as above.¶
Server -> client PUSH_PROMISE in current stream: PUSH_PROMISE - END_STREAM Promised Stream 4 HEADER BLOCK :method = GET :scheme = https :path = /tqs/2718281828459/uq/101 host = alto.example.com accept = application/alto-error+json, application/alto-costmap+json Server -> client content Stream 4: HEADERS + END_STREAM + END_HEADERS :status = 200 content-type = application/alto-costmap+json content-length = TBD DATA + END_STREAM { "meta" : { "dependent-vtags" : [{ "resource-id": "my-network-map", "tag": "da65eca2eb7a10ce8b059740b0b2e3f8eb1d4785" }], "cost-type" : { "cost-mode" : "numerical", "cost-metric": "routingcost" }, "vtag": { "resource-id" : "my-routingcost-map", "tag" : "3ee2cb7e8d63d9fab71b9b34cbf764436315542e" } }, "cost-map" : { "PID1": { "PID1": 1, "PID2": 5, "PID3": 10 }, "PID2": { "PID1": 5, "PID2": 1, "PID3": 15 }, "PID3": { "PID1": 20, "PID2": 15 } } } Server -> client PUSH_PROMISE in current stream: PUSH_PROMISE - END_STREAM Promised Stream 6 HEADER BLOCK :method = GET :scheme = https :path = /tqs/2718281828459/uq/102 host = alto.example.com accept = application/alto-error+json, application/merge-patch+json Server -> client content Stream 6 HEADERS + END_STREAM + END_HEADERS :status = 200 content-type = application/merge-patch+json content-length = TBD DATA + END_STREAM { ...}¶
Among the CRUD operations, a client can add to or delete itself from the receiver set of a transport queue. It can also read the status of the receiver set.¶
Creat: A client can add itself in the receiver set by using the HTTP PUT method: DELETE transport-queue/rs/self¶
Read: A client can see only itself in the receiver set. The appearance of self in the receiver set (read does not return not exists) is an indication that push starts.¶
Delete: A client can delete itself (stops receiving push) either explicitly or implicitly.¶
A main benefit of using HTTP/2 for ALTO is to take advantage of HTTP/2 streams. In particular, the objectives of ALTO/H2 include:¶
To realize the objectives specified in the preceding section, ALTO/H2 MUST satisfy the following stream management requirements in all 4 phases specified in the next 4 subsections.¶
Each request to create a transport queue (POST) MUST choose a new client selected stream ID (SID_tq), with the following requirements:¶
DELETE to close a transport queue (SID_tq) MUST be sent in SID_tq, with the following requirements:¶
The request and response MUST satisfy the following requirements:¶
The server push MUST satisfy the following requirements:¶
Extending the IRD example in Section 8.1 of [RFC8895], below is the IRD of an ALTO server supporting ALTO base protocol, ALTO/SSE, and ALTO/H2.¶
In particular,¶
"my-network-map": { "uri": "https://alto.example.com/networkmap", "media-type": "application/alto-networkmap+json", }, "my-routingcost-map": { "uri": "https://alto.example.com/costmap/routingcost", "media-type": "application/alto-costmap+json", "uses": ["my-networkmap"], "capabilities": { "cost-type-names": ["num-routingcost"] } }, "my-hopcount-map": { "uri": "https://alto.example.com/costmap/hopcount", "media-type": "application/alto-costmap+json", "uses": ["my-networkmap"], "capabilities": { "cost-type-names": ["num-hopcount"] } }, "my-filtered-cost-map": { "uri": "https://alto.example.com/costmap/filtered/constraints", "media-type": "application/alto-costmap+json", "accepts": "application/alto-costmapfilter+json", "uses": ["my-networkmap"], "capabilities": { "cost-type-names": ["num-routingcost", "num-hopcount"], "cost-constraints": true } }, "my-simple-filtered-cost-map": { "uri": "https://alto.example.com/costmap/filtered/simple", "media-type": "application/alto-costmap+json", "accepts": "application/alto-costmapfilter+json", "uses": ["my-networkmap"], "capabilities": { "cost-type-names": ["num-routingcost", "num-hopcount"], "cost-constraints": false } }, "my-props": { "uri": "https://alto.example.com/properties", "media-type": "application/alto-endpointprops+json", "accepts": "application/alto-endpointpropparams+json", "capabilities": { "prop-types": ["priv:ietf-bandwidth"] } }, "my-pv": { "uri": "https://alto.example.com/endpointcost/pv", "media-type": "multipart/related; type=application/alto-endpointcost+json", "accepts": "application/alto-endpointcostparams+json", "capabilities": { "cost-type-names": [ "path-vector" ], "ane-properties": [ "maxresbw", "persistent-entities" ] } }, "update-my-costs": { "uri": "https://alto.example.com/updates/costs", "media-type": "text/event-stream", "accepts": "application/alto-updatestreamparams+json", "uses": [ "my-network-map", "my-routingcost-map", "my-hopcount-map", "my-simple-filtered-cost-map" ], "capabilities": { "incremental-change-media-types": { "my-network-map": "application/json-patch+json", "my-routingcost-map": "application/merge-patch+json", "my-hopcount-map": "application/merge-patch+json" }, "support-stream-control": true } }, "update-my-costs-h2": { "uri": "https://alto.example.com/updates-h2/costs", "media-type": "application/alto-h2", "accepts": "application/alto-updatestreamparams+json", "uses": [ "my-network-map", "my-routingcost-map", "my-hopcount-map", "my-simple-filtered-cost-map" ], "capabilities": { "incremental-change-media-types": { "my-network-map": "application/json-patch+json", "my-routingcost-map": "application/merge-patch+json", "my-hopcount-map": "application/merge-patch+json" }, "support-stream-control": true } }, "update-my-props": { "uri": "https://alto.example.com/updates/properties", "media-type": "text/event-stream", "uses": [ "my-props" ], "accepts": "application/alto-updatestreamparams+json", "capabilities": { "incremental-change-media-types": { "my-props": "application/merge-patch+json" }, "support-stream-control": true } }, "update-my-pv": { "uri": "https://alto.example.com/updates/pv", "media-type": "text/event-stream", "uses": [ "my-pv" ], "accepts": "application/alto-updatestreamparams+json", "capabilities": { "incremental-change-media-types": { "my-pv": "application/merge-patch+json" }, "support-stream-control": true } }¶
Note that it is straightforward for an ALTO sever to run HTTP/2 and support concurrent retrieval of multiple resources such as "my-network-map" and "my-routingcost-map" using multiple HTTP/2 streams with the need to introducing ALTO/H2.¶
The resource "update-my-costs-h2" provides an ALTO/H2 based connection, and this is indicated by the media-type "application/alto-h2". For an ALTO/H2 connection, the client can send in a sequence of control requests using media type application/alto-updatestreamparams+json. The server creates HTTP/2 streams and pushes updates to the client.¶
The properties defined in this document present no security considerations beyond those in Section 15 of the base ALTO specification [RFC7285].¶
IANA will need to register the alto-h2 media type under ALTO registry as defined in [RFC7285].¶
The authors of this document would also like to thank many for the reviews and comments.¶
This draft is focusing on HTTP/2 enhancement of the ALTO protocol and the design takes advantage of HTTP/2 design features such as parallel transfer and respects HTTP/2 semantics (e.g., PUSH_PROMISE). Since QUIC and HTTP/3 respectively are coming up for various protocols on the Internet it is understandable that the question arises, if ATLO could also take advantage of the advantages of HTTP/3. QUIC can be seen as a replacement for TCP+TLS+HTTP2. HTTP/3 bases on the QUIC transport protocol and uses UDP instead of a TCP connection.¶
QUIC has been developed by the IETF QUIC Working Group with the following goals:¶
If HTTP/3 is not supported, it automatically runs on HTTP/2. The prerequisite for HTTP/3 is that both client and server support it.¶
The basic assumption is that an implementation that runs on HTTP/2 should also run-on HTTP/3. This should be transparent. HTTP/3 uses "well known port" UDP 443 analogous to TCP 443. The network between client and server must not filter HTTP/3.¶
Since many applications still using HTTP/2 it is mandatory for ALTO to support this protocol first. This ensures compatibility. Therefore, this document describes the update of ALTO from HTTP/1.x to HTTP/2. The usage of HTTP/3 will be described in a separate document so that compatibility of ALTO with HTTP/3 will be ensured in a later stage.¶