RFC 3117
On the Design of Application Protocols
RFC 3117: On the Design of Application Protocols ๐
okay bestie, this is RFC 3117, "On the Design of Application Protocols," from the Network Working Group ๐ It's written by M. Rose of Dover Beach Consulting, Inc., and it's dated November 2001. The category is Informational, so it's giving "here's some useful info" and not "this is a standard" ๐ค It doesn't list anything it obsoletes or updates in its title block.
Status of This Memo โจ
This memo gives information to the Internet community. It does not specify an Internet standard of any kind, and distribution of it is unlimited ๐ธ
Copyright Notice ๐ฆ
Copyright (C) The Internet Society (2001). All Rights Reserved.
Abstract ๐
This memo talks about the design principles behind the Blocks eXtensible eXchange Protocol (BXXP). BXXP is a generic application protocol framework for connection-oriented, asynchronous interactions. ngl it's giving main character energy, because the framework lets simultaneous and independent exchanges happen inside one application user-identity, and it supports both textual and binary messages ๐ฅ
Table of Contents ๐ง
1. A Problem 19 Years in the Making . . . . . . . . . . . . . . . 3
2. You can Solve Any Problem... . . . . . . . . . . . . . . . . . 6
3. Protocol Mechanisms . . . . . . . . . . . . . . . . . . . . . 8
3.1 Framing . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.2 Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3 Reporting . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.4 Asynchrony . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.5 Authentication . . . . . . . . . . . . . . . . . . . . . . . . 12
3.6 Privacy . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.7 Let's Recap . . . . . . . . . . . . . . . . . . . . . . . . . 13
4. Protocol Properties . . . . . . . . . . . . . . . . . . . . . 14
4.1 Scalability . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.2 Efficiency . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.3 Simplicity . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.4 Extensibility . . . . . . . . . . . . . . . . . . . . . . . . 15
4.5 Robustness . . . . . . . . . . . . . . . . . . . . . . . . . . 16
5. The BXXP Framework . . . . . . . . . . . . . . . . . . . . . . 17
5.1 Framing and Encoding . . . . . . . . . . . . . . . . . . . . . 17
5.2 Reporting . . . . . . . . . . . . . . . . . . . . . . . . . . 19
5.3 Asynchrony . . . . . . . . . . . . . . . . . . . . . . . . . . 19
5.4 Authentication . . . . . . . . . . . . . . . . . . . . . . . . 21
5.5 Privacy . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.6 Things We Left Out . . . . . . . . . . . . . . . . . . . . . . 21
5.7 From Framework to Protocol . . . . . . . . . . . . . . . . . . 22
6. BXXP is now BEEP . . . . . . . . . . . . . . . . . . . . . . . 23
7. Security Considerations . . . . . . . . . . . . . . . . . . . 23
References . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 26
Full Copyright Statement . . . . . . . . . . . . . . . . . . . . . 27
1. A Problem 19 Years in the Making ๐
okay so SMTP [1] is basically the perfect application protocol ๐ญ it solves a big, important problem in a super minimalist way. It's simple enough that an entry-level implementation can fit on one or two screens of code, and it's flexible enough to be the base for really powerful products in a robust, competitive market. Modulo a few oddities (e.g., SAML), the design is well thought out and the spec is well-written and mostly self-contained. Honestly there's very little about good application protocol design that you can't learn by reading the SMTP spec ๐
Unfortunately, there's one little problem ๐ SMTP was first published in 1981, and since then a lot of application protocols have been designed for the Internet, but there hasn't been a lot of reuse going on. You'd expect that if the protocols were all wildly different, but they aren't. Most are surprisingly similar in how they behave, even though the actual details vary a lot.
In late 1998, Carl Malamud and I were sitting down to review the Blocks architecture, and we realized we needed a protocol for exchanging Blocks. The conventional wisdom is that when you need an application protocol, there are four ways to go:
- find an existing exchange protocol that (more or less) does what you want;
- define an exchange model on top of the world-wide web infrastructure that (more or less) does what you want;
- define an exchange model on top of the electronic mail infrastructure that (more or less) does what you want; or,
- define a new protocol from scratch that does exactly what you want.
an engineer can make reasoned arguments about the merits of each of these approaches ๐ค Here's the process we followed...
The most appealing option is to find an existing protocol and use that. (In other words, we'd rather "buy" than "make".) So we did a survey of many existing application protocols and found that none of them were a good match for the semantics of the protocol we needed ๐ค
For example, most application protocols are oriented toward client/server behavior, and they emphasize the client pulling data from the server. Blocks is different: a client usually pulls data from the server, but it also may ask the server to asynchronously push (new) data to it โจ Clearly, we could mutate a protocol such as FTP [2] or SMTP into what we wanted, but by the time we did all that, the base protocol and our protocol would have more differences than similarities. In other words, the cost of modifying an off-the-shelf implementation becomes comparable with starting from scratch. ngl that's a big mood ๐
Another approach is to use HTTP [3] as the exchange protocol and define the rules for data exchange over it. For example, IPP [4] (the Internet Printing Protocol) does this. The basic idea is that HTTP defines the rules for exchanging data, and then you define the data's syntax and semantics. Because you inherit the entire HTTP infrastructure (e.g., HTTP's authentication mechanisms, caching proxies, and so on), there's less for you to invent (and code!) ๐ง Or, conversely, you might view the HTTP infrastructure as too helpful. As an added bonus, if you decide your protocol runs over port 80, you may be able to sneak your traffic past older firewalls, at the cost of port 80 saturation ๐
HTTP is lowkey iconic in a lot of ways: it's ubiquitous, it's familiar, and there are a lot of tools available for developing HTTP-based systems. Another good thing is that it uses MIME [5] for encoding data ๐ซถ
Unfortunately for us, even with HTTP 1.1 [6], there still wasn't a good fit ๐ญ Because of the highly-desirable goal of maintaining compatibility with the original HTTP, HTTP's framing mechanism isn't flexible enough to support server-side asynchronous behavior, and its authentication model isn't similar to other Internet applications.
Mapping IPP onto HTTP 1.1 shows the first issue. For example, the IPP server is supposed to signal its client when a job completes. Since the HTTP client has to originate all requests, and since the decision to close a persistent connection in HTTP is unilateral, the best the IPP specification can do is describe this functionality in a non-deterministic fashion ๐ฅฒ
The IPP mapping onto HTTP also shows that even subtle shifts in behavior have unintended consequences. For example, requests in IPP are typically much larger than those seen by many HTTP server implementations, which causes oddities in many HTTP servers (e.g., requests are sometimes silently truncated). The lesson is that HTTP's framing mechanism is very rigid about its view of the request/response model ๐ซ
Lastly, given our belief that the port field of the TCP header isn't a constant 80, we were immune to the seductive allure of sneaking our traffic past unwary site administrators ๐ โโ๏ธ
The third choice, layering the protocol on top of email, was attractive. Unfortunately, our application has a lot of interactivity with relatively small response times. So this left us with the final alternative: defining a protocol from scratch ๐ฆ
To begin, we figured our requirements, while a little more stringent than most, could fit inside a framework suitable for a large number of future application protocols. The trick is to avoid the kitchen-sink approach. (Dave Clark has a saying: "One of the roles of architecture is to tell you what you can't do.") ๐
2. You can Solve Any Problem... โจ
...if you're willing to make the problem small enough. ๐ง
okay so our biggest move is shrinking the problem down to application protocols with three specific features ๐ :
- they are connection-oriented;
- they use requests and responses to exchange messages; and,
- they allow for asynchronous message exchange.
Let's go through each one, bestie ๐ซถ
First up, we're only looking at connection-oriented application protocols (think ones that run on top of TCP [7]) ๐ฆ The other branch of the taxonomy is connectionless, which is for protocols that don't want the delay or overhead of setting up and keeping a reliable stream. Like, most DNS [8] traffic is one request and one response, and both fit inside a single IP datagram. So it makes sense to build a basic reliability service into the application protocol itself, above the transport layer ๐ธ
Second, we're only going to consider message-oriented application protocols. In our lexicon, a "message" is just structured data exchanged between loosely-coupled systems ๐ The other branch, tightly-coupled systems, uses remote procedure calls as the exchange paradigm. Unlike connection-oriented versus connectionless, loosely-coupled versus tightly-coupled is more of a continuous spectrum. Thankfully, the edges are pretty sharp ๐ฅ
For example, NFS [9] is a tightly-coupled system that uses RPCs. In a properly-configured LAN, a remote disk you reach over NFS is basically indistinguishable from a local disk ๐คฏ To pull that off, tightly-coupled systems care a lot about latency. So most (but not all) of them use connection-less RPC mechanisms, and most tend to be built as operating system functions rather than user-level programs. (In some environments, tightly-coupled systems are single-purpose servers running on hardware specifically optimized for that one function ๐)
Finally, we're going to think about application protocols that exchange messages asynchronously โจ The classic client/server model is that the client sends a request and the server sends a response. If you think of requests as "questions" and responses as "answers," then the server only answers the questions it's asked, and it never asks any questions of its own ๐ We need to support a more general model, peer-to-peer. In this model, one peer might be the "client" and the other the "server" for a given transaction, but for the next transaction the two peers might swap roles. Main character energy ๐
Here's the plot twist: the client/server model is a proper subset of the peer-to-peer model ๐ญ It's totally acceptable for a particular application protocol to dictate that the peer that establishes the connection always acts as the client (initiating requests), and that the peer that listens for incoming connections always acts as the server (issuing responses to requests) ๐
There are quite a few existing application domains that don't fit our requirements, like nameservice (via the DNS), fileservice (via NFS), and multicast-enabled applications such as distributed video conferencing, and so on ๐ However, there are a LOT of application domains that do fit, like electronic mail, file transfer, remote shell, and the world-wide web ๐ So the bet we're placing going forward is that there will keep being reasons to define protocols that fit inside our framework ๐
3. Protocol Mechanisms ๐ท
Next, we look at the tasks an application protocol has to perform and how it goes about doing them. An exhaustive exposition might identify a dozen (or so) areas, but the ones we care about are:
- framing, which tells how the beginning and ending of each message is delimited;
- encoding, which tells how a message is represented when it's exchanged;
- reporting, which tells how errors are described;
- asynchrony, which tells how independent exchanges are handled;
- authentication, which tells how the peers at each end of the connection are identified and verified; and,
- privacy, which tells how the exchanges are protected against third-party interception or modification.
A notable absence from this list is naming. We'll explain why later on ๐ซฃ
3.1 Framing ๐งฑ
There are three commonly used approaches to delimiting messages: octet-stuffing, octet-counting, and connection-blasting ๐
Octet-stuffing is what SMTP does. SMTP commands are line-oriented (each command ends in a CR-LF pair) ๐
When an SMTP peer sends a message, it first transmits the DATA command, then the message, and then a . (dot) followed by a CR-LF. If the message contains any lines that begin with a dot, the sending SMTP peer sends two dots. Similarly, when the other SMTP peer receives a line that begins with a dot, it discards the dot, and if the line is empty, it knows it's received the entire message. The perk of octet-stuffing is that you don't need the entire message in front of you before you start sending it. The downside is that it's slow, because both the sender and receiver have to scan each line of the message to see if they need to transform it ๐ฉ
DATA
<message lines>
.
Octet-counting is what HTTP does. HTTP commands consist of a request line followed by headers and a body. The headers contain an octet count indicating how large the body is. Octet-counting is basically the inverse of octet-stuffing ๐ Before you can start sending a message, you need to know the length of the whole thing. But once you start sending or receiving, you don't have to look at the content of the message at all โจ
Connection-blasting is what FTP does. FTP commands are line-oriented, and when it's time to exchange a message, a brand new TCP connection gets established just to transmit that message ๐ฆ Both octet-counting and connection-blasting have the property that messages can be arbitrary binary data. The drawback of connection-blasting is that the peers need to communicate IP addresses and TCP port numbers, which may be "transparently" altered by NATS [10] and network bugs ๐ตโ๐ซ Also, if the messages being exchanged are small (say less than 32k), then the overhead of establishing a connection for each message adds significant latency during data exchange ๐ข
3.2 Encoding ๐ฌ
There are lots of schemes for encoding data (and many more encoding schemes have been proposed than are actually in use). Thankfully, only a few are burning brightly on the radar ๐ฅ
The messages exchanged using SMTP are encoded using the 822-style [11] ๐ The 822-style divides a message into textual headers and an unstructured body. Each header consists of a name and a value, and is terminated with a CR-LF pair. An additional CR-LF separates the headers from the body.
This is the structure HTTP uses to indicate the length of the body for framing purposes ๐ง More formally, HTTP uses MIME, an application of the 822-style, to encode both the data itself (the body) and information about the data (the headers). So although HTTP is commonly viewed as a retrieval mechanism for HTML [12], it's really a retrieval mechanism for objects encoded using MIME. Most of those objects are either HTML pages or referenced objects such as GIFs ๐ผ๏ธ
3.3 Reporting ๐ฃ
An application protocol needs a way to convey error information between peers ๐จ The first formal method for this was defined by SMTP's "theory of reply codes." The basic idea is that an error is identified by a three-digit string, and each position has a different significance:
- the first digit: indicating success or failure, either permanent or transient;
- the second digit: indicating the part of the system reporting the situation (e.g., the syntax analyzer); and,
- the third digit: identifying the actual situation.
Operational experience with SMTP showed that the range of error conditions is bigger than can be comfortably encoded in a three-digit string. (You can only report on 10 different things going wrong for any given part of the system, which is not a lot, bestie ๐ค) So [13] provides a convenient mechanism for extending the number of values that can occur in the second and third positions ๐
Virtually all of the application protocols we've discussed so far use the three-digit reply codes. Honestly, though, there's less coordination between the designers of different application protocols than most would care to admit ๐ (A variation on the theory of reply codes is used by IMAP [14], which provides the same information using a different syntax.)
On top of a reply code, most application protocols also send a textual diagnostic that's meant for human consumption, not machines ๐ (More accurately, the textual diagnostic is meant for people who can read a widely used variant of the English language. Iykyk ๐) Since reply codes reflect both positive and negative outcomes, there have been some innovative uses of the text accompanying positive responses, like prayer wheels [39] ๐๐ก Regardless, some of the more modern application protocols include a language localization parameter for the diagnostic text ๐
Finally, since reply codes were introduced in 1981, two unresolved criticisms have been raised ๐ซ
- a reply code is used both to signal the outcome of an operation and a change in the application protocol's state; and,
- a reply code doesn't specify whether the associated textual diagnostic is destined for the end-user, administrator, or programmer.
3.4 Asynchrony โณ
Okay bestie, few application protocols today let independent exchanges share the same connection ๐ซถ. The more widely implemented trick is pipelining, like command pipelining [15] in SMTP or persistent connections in HTTP 1.1. Pipelining lets a client send several requests to a server, but the server still has to process them one after another ๐ข. (Fun fact: a protocol needs to explicitly support pipelining, because without clear guidance a lot of implementors build systems that don't handle it properly. Typically, one error in a request causes every later request in the pipeline to get tossed ๐.)
Pipelining is a powerful way to cut down network latency โจ. For example, without persistent connections, HTTP's framing is really closer to connection-blasting than octet-counting, and it suffers from the same latency and efficiency problems ๐ญ.
Asynchrony also cuts server latency, on top of the pipelining effect, because multi-threaded implementations can process multiple requests at once ๐งต. But here's the catch: if you allow any form of asynchronous exchange, you also need support for parallelism, since exchanges aren't necessarily happening under the synchronous direction of a single peer ๐.
Now, when you allow parallelism, you also need a flow control mechanism ๐ฆ. Otherwise, one set of exchanges can hog all the bandwidth the transport layer provides (main character behavior, but make it rude ๐). And if a peer is resource-starved, it may not have enough buffers to receive a message, and then you get deadlock ๐. Yikes.
Flow control is usually handled at the transport layer ๐ฐ. TCP, for example, uses sequence numbers and a sliding window. Each receiver manages a sliding window that says how many data octets can be sent before the receiver gives further permission. But wait, it's now time for the second shoe to drop: segmentation ๐ . If you do flow control, you also need a segmentation mechanism to break messages into smaller pieces before sending, then reassemble them as they arrive ๐งฉ.
Both flow control and segmentation affect how the protocol does framing ๐ผ๏ธ. Earlier we defined framing as "how to tell the beginning and end of each message." Now we also need to identify independent messages, send messages only when flow control allows it, and segment them if they're bigger than the available window (or just too large for comfort ๐ ).
Segmentation affects framing in another way too ๐ . It loosens the octet-counting requirement that you know the length of the whole message before sending any of it. With segmentation, you can start sending segments before the whole message is ready. In HTTP 1.1, you can "chunk" (segment) data to get this perk ๐.
3.5 Authentication ๐
Perhaps for historical (or hysterical ๐) reasons, most application protocols don't do authentication. They don't verify the identity of the peers on the connection, or the authenticity of the messages being exchanged. Or, if authentication does happen, it's specific to each protocol. For example, FTP and HTTP use totally different models and mechanisms for authenticating the initiator of a connection ๐ โโ๏ธ. (Separate from mainstream HTTP, there's a little-used variant [16] that authenticates the messages it exchanges ๐ค.)
A big part of the problem is that different security mechanisms optimize for different things: strength, scalability, or ease of deployment ๐ฏ. So a few years ago, SASL [17] (the Simple Authentication and Security Layer) was developed as a framework for authenticating protocol peers ๐งฐ. SASL lets you describe how an authentication mechanism works, like an OTP [18] (One-Time Password) exchange ๐. Then it's up to each protocol designer to specify how SASL exchanges get carried by the protocol. For example, [19] explains how SASL works with SMTP ๐ง.
HTTP is the notable exception to the SASL bandwagon ๐. It defines its own authentication mechanisms [20] instead. There's little reason SASL couldn't be introduced to HTTP, although to avoid certain race conditions, the persistent connection mechanism of HTTP 1.1 must be used ๐ง .
SASL has a really interesting feature ๐ค. Besides explicit protocol exchanges that authenticate identity, it can also use implicit information provided by the layer below. For example, if the connection runs over IPsec [21], then each peer's credentials are known and verified when the TCP connection is established ๐ชช.
And finally, as its name suggests, SASL can do more than authentication ๐ซ. Depending on which SASL mechanism is in use, message integrity or privacy services may also be provided ๐ก๏ธ.
3.6 Privacy ๐คซ
HTTP was the first widely used protocol to use a transport security protocol to encrypt the data sent over the connection ๐. The current version of this mechanism, TLS [22], is available to all application protocols, like SMTP and ACAP [23] (the Application Configuration Access Protocol) ๐.
The big difference between the original mechanism and TLS is about provisioning, not technology ๐ ๏ธ. In the original approach, a world-wide web server listened on two ports: one for plaintext traffic and one for secured traffic. Today's conventions are different. A server implementing an application protocol that's specified as TLS-enabled (e.g., [24] and [25]) listens on a single port for plaintext traffic. Once a connection is established, the use of TLS on that connection is negotiable ๐ค.
Last but not least, SASL and TLS are both about "transport security," not "object security" ๐จ. They focus on providing security properties for the actual communication. They don't provide any security properties for the data exchanged independently of that communication ๐ฆ. Know the difference, bestie ๐.
3.7 Let's Recap ๐
Let's compare the properties of the three main connection-oriented application protocols in use today ๐:
Mechanism ESMTP FTP HTTP1.1
-------------- ----------- --------- -------------
Framing stuffing blasting counting
Encoding 822-style binary MIME
Reporting 3-digit 3-digit 3-digit
Asynchrony pipelining none pipelining
and chunking
Authentication SASL user/pass user/pass
Privacy SASL or TLS none TLS (nee SSL)
Notice that the username/password mechanisms used by FTP and HTTP are totally different, with one exception: both can be called a "username/password" mechanism ๐คทโโ๏ธ.
These three choices are broadly representative ๐. As more protocols get considered, the patterns keep getting reinforced. For example, POP [26] uses octet-stuffing, but IMAP uses octet-counting, and so on ๐.
4. Protocol Properties ๐
When we design an application protocol, there are a few properties we should keep an eye on ๐๏ธ.
4.1 Scalability ๐
A well-designed protocol is scalable ๐ฆ.
Since few application protocols support asynchrony, a common trick is for a program to open multiple simultaneous connections to one destination ๐โโ๏ธ. The theory is that this reduces latency and increases throughput. The reality? Both the transport layer and the server see each connection as an independent instance of the application protocol, and that causes problems ๐ฉ.
On the transport layer side, TCP uses adaptive algorithms to transmit data efficiently as network conditions change ๐. But what TCP learns is limited to each connection. So if you have multiple TCP connections, you have to go through the same learning process multiple times, even if you're talking to the same host ๐. This doesn't just add unnecessary traffic spikes to the network, because TCP uses a slow-start algorithm when establishing a connection. The program still sees the extra latency too ๐ฅฒ. To handle the fact that a lack of asynchrony pushes implementors into sloppy use of the transport layer, network protocols are now being provisioned with increasing sophistication, e.g., RED [27]. Further, suggestions are being considered for modifying TCP implementations to reduce concurrent learning, e.g., [28] ๐งช.
On the server side, each incoming connection has to be dispatched and (probably) authenticated against the same resources ๐ช. So server overhead goes up based on the number of connections established, rather than the number of remote users. The same fairness issues come up too: it's much harder for servers to allocate resources on a per-user basis when one user can cause an arbitrary number of connections to pound on the server ๐ฅ.
Another important part of scalability is the relative number of clients and servers โ๏ธ. (This is true even in the peer-to-peer model, where a peer can act as both client and server ๐.) Typically, there are way more client peers than server peers. In that case, functional requirements should be shifted from the servers onto the clients ๐ซธ. The reason is that a server is likely to be interacting with multiple clients, and this functional shift makes it easier to scale ๐งโโ๏ธ.
4.2 Efficiency โก
A well-designed protocol is efficient ๐๏ธ.
For example, although a compelling argument can be made that octet-stuffing leads to more elegant implementations than octet-counting, experience shows that octet-counting consumes far fewer cycles ๐งฎ.
Sadly, we sometimes have to give up efficiency to satisfy other properties ๐. For example, 822 (and MIME) use textual headers. We could definitely define a more efficient representation for headers if we were willing to limit which header names and values could be used. In this case, extensibility is viewed as more important than efficiency ๐งฉ. Of course, if we were designing a network protocol instead of an application protocol, we'd make these trade-offs with a razor that has a different edge ๐ช.
4.3 Simplicity ๐ซง
A well-designed protocol is simple โจ.
Here's a good rule of thumb ๐: a poorly designed application protocol is one where doing something basic is just as "challenging" as doing something complex ๐. Easy things should be easy to do, and hard things should be harder to do. The reason is simple: the pain should be proportional to the gain ๐ .
Another rule of thumb: if an application protocol has two ways of doing the exact same thing, there's a problem somewhere in the architecture underlying the design ๐ฉ.
Hopefully, simple doesn't mean simple-minded ๐ง . Something well-designed accommodates everything in the problem domain, even the troublesome things at the edges ๐ธ. What makes the design simple is that it does this in a consistent fashion. Typically, that leads to an elegant design ๐.
4.4 Extensibility โจ
okay so a well-designed protocol is extensible ๐ that's the whole vibe ๐ฆ
Even the most brilliant application protocol designers are going to run into problems nobody saw coming ๐ญ so the protocol needs hooks that people can use to add features or customize behavior ๐ that means the protocol is evolutionary, and there has to be a way for implementations from different steps along that path to negotiate which extensions they'll actually use ๐ซถ
BUT (and this is important, bestie) don't fall into the extensibility trap ๐ the hooks shouldn't be aimed at half-baked future requirements that don't exist yet ๐ above all, the hooks should be simple โจ
Good design goes a long way toward cutting down on the need for extensibility ๐ for example, SMTP didn't have an extension framework at first, and it took ten years of experience before its excellent design got changed ๐ Telnet [29], on the other hand, is a poorly-designed protocol that just can't work without being built around the idea of extensions ๐ฌ
4.5 Robustness ๐ง
a well-designed protocol is robust ๐
ngl robustness and efficiency are often at odds ๐ like, defaults are super handy because they shrink packet sizes and cut down on processing time, but they tend to encourage implementation errors ๐
now here's the plot twist ๐ญ Postel's robustness principle ("be conservative in what you send, liberal in what you accept") often causes deployment problems, which is so counterintuitive ๐คฏ why? when a new implementation first gets fielded, it's probably only going to meet a subset of the existing implementations ๐ธ if those existing implementations follow the robustness principle, then the new implementation's errors will likely slip right past them unnoticed ๐ so the new implementation looks like it's working, but only sort of, because it isn't widely deployed yet โจ this keeps happening with several new implementations ๐ eventually, those not-quite-correct implementations bump into implementations that are less liberal than the first group ๐ฅ you can figure out what happens next, iykyk ๐
so, explicit consistency checks in a protocol are super useful ๐ซถ even if they cost implementers some overhead ๐
5. The BXXP Framework ๐
okay finally, the money shot ๐ฅ here's what we actually did ๐
we defined an application protocol framework called BXXP (the Blocks eXtensible eXchange Protocol) ๐ฆ it's a "framework" and not an application protocol because we give you all the mechanisms we talked about earlier without actually specifying what kind of messages get exchanged ๐ so if someone else needs an application protocol that's connection-oriented and asynchronous, they can start with BXXP โจ then it's their job to define the last 10% of the application protocol, the part that does, as we say, "the useful work" ๐
so, what does BXXP look like? ๐
Mechanism BXXP
-------------- ----------------------------------------
Framing counting, with a trailer
Encoding MIME, defaulting to text/xml
Reporting 3-digit and localized textual diagnostic
Asynchrony channels
Authentication SASL
Privacy SASL or TLS
5.1 Framing and Encoding ๐ธ
framing in BXXP looks a lot like SMTP or HTTP ๐ซถ there's a command line that marks the beginning of the frame, and then there's a MIME object (headers and body) ๐ unlike SMTP, BXXP uses octet-counting, but unlike HTTP, the command line is where you find the size of the payload ๐ and finally, there's a trailer after the MIME object to help detect framing errors ๐
okay but the BXXP command line has SO much info ๐ญ it tells you:
- what kind of message is in this frame;
- whether there's more to the message than what's in this frame (a continuation flag);
- how to tell the message in this frame apart from other messages (a message number);
- where the payload sits in the sliding window (a sequence number), along with how many octets are in the payload of this frame; and,
- which part of the application should get the message (a channel number).
(the command line is textual and ends in a CR-LF pair, and the arguments are separated by a space ๐ง)
since you need all of this to process a frame, we put it all in one easy-to-parse spot โจ could you come up with a more efficient encoding? probably ๐คทโโ๏ธ but the command line is a tiny part of the frame, so optimizing it wouldn't get you much bang for your buck ๐ also, framing is at the heart of BXXP, so the frame format has several consistency checks that catch the majority of programming errors ๐ (the combo of a sequence number, an octet count, and a trailer makes for really robust error detection ๐ฅ)
another trick is in the headers ๐ because the command line already holds all the framing info, the headers can carry minimal MIME information (like Content-Type) ๐ usually, though, the headers are empty ๐ซง that's because the BXXP default payload is XML [30] ๐ (actually, it's "Content-Type: text/xml" with binary transfer encoding ๐ค)
we chose XML as the default because it gives you a simple mechanism for nested, textual representations ๐ (alas, the 822-style encoding doesn't easily support nesting ๐) by design, XML isn't optimized for compact representations ๐ but that's okay, because we're focusing on loosely-coupled systems, and besides, there are efficient XML parsers available ๐ซถ also, there's a fair amount of anecdotal experience (and we'll stress the word "anecdotal") that if you have any kind of compression (at the link layer or during encryption), XML encodings squeeze down nicely ๐๏ธโจ
ngl, using XML is probably the most controversial part of BXXP ๐ฌ after all, there are more efficient representations out there ๐ซ we agree! but the real issue isn't efficiency, it's ease of use ๐ there are a LOT of people who grok the XML thing, and a LOT of XML tools out there ๐งฐ the pain of recreating that social infrastructure far outweighs any benefit of devising a new representation ๐ญ so if the "make" option is too expensive, is there something else we can "buy" besides XML? well, there's ASN.1/BER (just kidding) ๐
back in the early days of SNMP [31], which does use ASN.1, the same issues came up ๐ฐ๏ธ in the end, the working group agreed that using ASN.1 for SNMP was axiomatic ๐๏ธ but not because anyone thought ASN.1 was the most efficient, or the easiest to explain, or even well liked ๐ ASN.1 got axiomatic status because the working group decided it was not going to spend the next three years explaining an alternative encoding scheme to the developer community ๐ฉโจ
so, and we apologize for appealing to dogma ๐, using XML as the favored encoding scheme in BXXP is axiomatic ๐
5.2 Reporting ๐ฃ
we use 3-digit error codes with a localized textual diagnostic ๐ (each peer specifies a preferred ordering of languages ๐ฃ๏ธ)
also, the reply to a message gets flagged as either positive or negative โ โ this makes it easy to signal success or failure, and it gives the receiving peer some freedom in how much parsing it wants to do on failure ๐ซถ๐
5.3. Asynchrony
okay so, real talk: despite all the lessons SMTP and HTTP taught us, there's barely any field experience to lean on for BXXP's asynchrony features ๐ญ there were a few efforts in 1998 around application layer framing, like [32], but none of them made it to orbit ๐ธ
so here's what we did ๐ frames get exchanged inside a "channel". every channel has a "profile" attached, and that profile defines the syntax and semantics of the messages going over that channel ๐
channels are giving both an extensibility mechanism for BXXP AND the basis for parallelism โจ remember the last parameter on the command line of a BXXP frame? the "part of the application" that receives the message is identified by a channel number. iconic ๐
a profile is defined using a "Profile Registration" template. the template says how the profile gets identified (using a URI [33]), what kinds of messages get exchanged, and the syntax and semantics of those messages ๐ฆ when you create a channel, you name a profile and maybe piggyback your first message on it. if the channel gets created successfully, you get a positive response back. otherwise you get a negative response explaining why ๐ซถ
the easiest way to see how channels work as an extensibility mechanism is to look at what happens when a session starts ๐ง each BXXP peer immediately sends a greeting on channel zero, listing the profiles it supports. (channel 0 is for channel management, and it's automatically created when a session is opened.) if you want transport security, the very first thing you do is create a channel that negotiates transport security, and once that channel exists, you tell it to do its thing ๐ next, if you want to authenticate, you create a channel that performs user authentication, and once it's up, you tell it to get busy. then you create one or more channels for data exchange. this whole process is called "tuning" ๐ธ once you've tuned the session, you use the data exchange channels to do "the useful work" ๐
the first channel that gets started successfully has a little trick to it: when you ask to start that channel, you're allowed to specify a "service name" with it ๐ this lets a server with multiple configurations pick one based on what the client suggested. (a useful analogy is HTTP 1.1's Host: header.) if the server accepts the "service name", that configuration gets used for the rest of the session ๐
to allow parallelism, BXXP lets you use multiple channels at the same time โญ each channel processes its messages serially, but there are no constraints on the processing order between different channels. so in a multi-threaded implementation, each channel maps to its own thread. main character energy ๐
that's the most general case, obviously. for one reason or another, an implementor might not be able to support it. so BXXP allows for both positive and negative replies when a message is sent. if you want the classic client/server model, the client program should simply reject any new message the server sends. this effectively throttles any asynchronous messages from the server ๐
now we need mechanisms for segmentation and flow control ๐ค for segmentation, we just put a "continuation" or "more to come" flag in the command line for the frame. for flow control, we introduced the notion of a "transport mapping".
what this means is that BXXP doesn't directly define how it sits on top of TCP. instead, it lists a bunch of requirements for how a transport service needs to support a BXXP session. then, in a separate document, we defined how you can use TCP to meet those requirements ๐งฉ
that second document pretty much says "use TCP directly", except it introduces a flow control mechanism for multiplexing channels over a single TCP connection. the mechanism is the same one TCP uses (sequence numbers and a sliding window). it's proven, and it can be trivially implemented by a minimal implementation of BXXP ๐ฆ
ngl, adding flow control is a burden for implementors. even though TCP's mechanism is conceptually simple, an implementor has to be really careful. for example, things like priorities, queue management, and the like should be addressed ๐ฌ still, we feel the benefits of allowing parallelism for intra-application streams are worth it. (besides, we believe few application implementors will actually code the BXXP framework directly. instead, we expect them to use third-party packages that implement BXXP ๐ )
5.4. Authentication
we use SASL ๐ if you successfully authenticate using a channel, there's a single user identity for each peer on that session. so authentication is per-session, not per-channel. this design decision means each session corresponds to exactly one user, no matter how many channels are open on it ๐ this matters because it lets service provisioning, like quality of service (e.g., as in [34]), be done at a per-user granularity ๐
5.5. Privacy
we use SASL and TLS ๐ if you successfully complete a transport security negotiation using a channel, then all traffic on that session is secured. confidentiality is per-session, not per-channel, just like authentication ๐
we defined a BXXP profile that's used to start the TLS engine ๐ง
5.6. Things We Left Out
we purposefully excluded two things that most application protocols have: naming and authorization ๐ โโ๏ธ
naming was left out of the framework because, outside of URIs, there isn't a commonly accepted framework for naming things. in our view, naming is a domain-specific problem for each application protocol. maybe URIs are appropriate for a particular problem domain, maybe not ๐คทโโ๏ธ so when an application protocol designer defines their own profile to do "the useful work", they'll have to deal with naming issues themselves. BXXP gives you a mechanism for identifying profiles and binding them to channels. it's up to you to define the profile and use the channel ๐ซถ
authorization was explicitly excluded from the framework too. every approach to authorization we've seen uses names to identify principals (i.e., targets and subjects). so if a framework doesn't include naming, it can't very well include authorization ๐ง
of course, application protocols do have to deal with naming and authorization. those are two of the issues the applications protocol designer addresses when defining a profile for use with BXXP โจ
5.7. From Framework to Protocol
so, how do you actually go about using BXXP? first, call it "BEEP", not "BXXP" (we'll explain why in a sec) ๐
first, get the BEEP core specification [35] and read it ๐ next, define your own profile. finally, grab one of the open source SDKs (in C, Java, or Tcl) and start coding ๐ป
the BEEP specification defines several profiles itself: a channel management profile, a family of profiles for SASL, and a transport security profile. there's also a second specification [36] that explains how a BEEP session maps onto a single TCP connection ๐
for a complete example of an application protocol defined using BEEP, look at reliable syslog [37]. this document shows the formula perfectly ๐ฐ
application protocol = BEEP + 1 or more profiles
+ authorization policies
+ provisioning rules (e.g., use of SRV RRs [38])
6. BXXP is now BEEP ๐
we started work on BXXP in the fall of 1998 ๐ the IETF formed a working group on BXXP in the summer of 2000 โ๏ธ although the working group made some enhancements to BXXP, three are the most notable:
- The payload default is
application/octet-stream. this is primarily for wire-efficiency. if you care about wire-efficiency, then you probably wouldn't be usingtext/xml... ๐ - One-to-many exchanges are supported (the client sends one message and the server sends back many replies) ๐ฅ
- BXXP is now called BEEP (more comic possibilities) ๐๐
7. Security Considerations ๐
okay this one's serious bestie ๐ญ consult Section [35]'s Section 8 for a discussion of BEEP-related security issues. go read it, no skipping ๐ซถ
References ๐โจ
okay so this is the bibliography, bestie, and it's giving history ๐ฐ๏ธ๐ every source the spec leans on is listed here so you can go find the originals. Each entry keeps its number, authors, title, and date exactly as written.
[1] Postel, J., "Simple Mail Transfer Protocol", STD 10, RFC 821,
August 1982.
[2] Postel, J. and J. Reynolds, "File Transfer Protocol", STD 9,
RFC 959, October 1985.
[3] Berners-Lee, T., Fielding, R. and H. Nielsen, "Hypertext
Transfer Protocol -- HTTP/1.0", RFC 1945, May 1996.
[4] Herriot, R., "Internet Printing Protocol/1.0: Encoding and
Transport", RFC 2565, April 1999.
[5] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
Extensions (MIME) Part One: Format of Internet Message Bodies",
RFC 2045, November 1996.
[6] Fielding, R., Gettys, J., Mogul, J., Nielsen, H., Masinter, L.,
Leach, P. and T. Berners-Lee, "Hypertext Transfer Protocol --
HTTP/1.1", RFC 2616, June 1999.
[7] Postel, J., "Transmission Control Protocol", STD 7, RFC 793,
September 1981.
[8] Mockapetris, P., "Domain names - concepts and facilities", STD
13, RFC 1034, November 1987.
[9] Microsystems, Sun., "NFS: Network File System Protocol
specification", RFC 1094, March 1989.
[10] Srisuresh, P. and M. Holdrege, "IP Network Address Translator
(NAT) Terminology and Considerations", RFC 2663, August 1999.
[11] Crocker, D., "Standard for the format of ARPA Internet text
messages", STD 11, RFC 822, August 1982.
[12] Berners-Lee, T. and D. Connolly, "Hypertext Markup Language -
2.0", RFC 1866, November 1995.
[13] Freed, N., "SMTP Service Extension for Returning Enhanced Error
Codes", RFC 2034, October 1996.
[14] Myers, J., "IMAP4 Authentication Mechanisms", RFC 1731,
December 1994.
[15] Freed, N., "SMTP Service Extension for Command Pipelining", RFC
2197, September 1997.
[16] Rescorla, E. and A. Schiffman, "The Secure HyperText Transfer
Protocol", RFC 2660, August 1999.
[17] Myers, J., "Simple Authentication and Security Layer (SASL)",
RFC 2222, October 1997.
[18] Newman, C., "The One-Time-Password SASL Mechanism", RFC 2444,
October 1998.
[19] Myers, J., "SMTP Service Extension for Authentication", RFC
2554, March 1999.
[20] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S.,
Leach, P., Luotonen, A. and L. Stewart, "HTTP Authentication:
Basic and Digest Access Authentication", RFC 2617, June 1999.
[21] Kent, S. and R. Atkinson, "Security Architecture for the
Internet Protocol", RFC 2401, November 1998.
[22] Dierks, T. and C. Allen, "The TLS Protocol Version 1.0", RFC
2246, January 1999.
[23] Newman, C. and J. Myers, "ACAP -- Application Configuration
Access Protocol", RFC 2244, November 1997.
[24] Hoffman, P., "SMTP Service Extension for Secure SMTP over TLS",
RFC 2487, January 1999.
[25] Newman, C., "Using TLS with IMAP, POP3 and ACAP", RFC 2595,
June 1999.
[26] Myers, J. and M. Rose, "Post Office Protocol - Version 3", STD
53, RFC 1939, May 1996.
[27] Braden, B., Clark, D., Crowcroft, J., Davie, B., Deering, S.,
Estrin, D., Floyd, S., Jacobson, V., Minshall, G., Partridge,
C., Peterson, L., Ramakrishnan, K., Shenker, S., Wroclawski, J.
and L. Zhang, "Recommendations on Queue Management and
Congestion Avoidance in the Internet", RFC 2309, April 1998.
[28] Touch, J., "TCP Control Block Interdependence", RFC 2140, April
1997.
[29] Postel, J. and J. Reynolds, "Telnet Protocol Specification",
STD 8, RFC 854, May 1983.
[30] World Wide Web Consortium, "Extensible Markup Language (XML)
1.0", W3C XML, February 1998, <http://www.w3.org/TR/1998/REC-
xml-19980210>.
[31] Case, J., Fedor, M., Schoffstall, M. and C. Davin, "Simple
Network Management Protocol (SNMP)", STD 15, RFC 1157, May
1990.
[32] World Wide Web Consortium, "SMUX Protocol Specification",
Working Draft, July 1998, <http://www.w3.org/TR/1998/WD-mux-
19980710>.
[33] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform
Resource Identifiers (URI): Generic Syntax", RFC 2396, August
1998.
[34] Waitzman, D., "IP over Avian Carriers with Quality of Service",
RFC 2549, April 1999.
[35] Rose, M., "The Blocks Extensible Exchange Protocol Core", RFC
3080, March 2001.
[36] Rose, M., "Mapping the BEEP Core onto TCP", RFC 3081, March
2001.
[37] New, D. and M. Rose, "Reliable Delivery for syslog", RFC 3195,
November 2001.
[38] Gulbrandsen, A., Vixie, P. and L. Esibov, "A DNS RR for
specifying the location of services (DNS SRV)", RFC 2782,
February 2000.
[39] <http://mappa.mundi.net/cartography/Wheel/>
Author's Address ๐
the author is Marshall T. Rose, and this is where you'd reach him if you ever wanted to talk BEEP ๐ฆ his contact details are below, exactly as written.
Marshall T. Rose
Dover Beach Consulting, Inc.
POB 255268
Sacramento, CA 95865-5268
US
Phone: +1 916 483 8878
EMail: mrose@dbc.mtview.ca.us
Full Copyright Statement โ๏ธ๐
this part is the legal stuff, so I'm keeping it word for word so nothing gets lost in translation ๐ซถ
Copyright (C) The Internet Society (2001). All Rights Reserved.
This document and translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it
or assist in its implementation may be prepared, copied, published
and distributed, in whole or in part, without restriction of any
kind, provided that the above copyright notice and this paragraph are
included on all such copies and derivative works. However, this
document itself may not be modified in any way, such as by removing
the copyright notice or references to the Internet Society or other
Internet organizations, except as needed for the purpose of
developing Internet standards in which case the procedures for
copyrights defined in the Internet Standards process must be
followed, or as required to translate it into languages other than
English.
The limited permissions granted above are perpetual and will not be
revoked by the Internet Society or its successors or assigns.
This document and the information contained herein is provided on an
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Acknowledgement ๐ธ
last but not least, the RFC Editor function gets its funding from the Internet Society, so a big thank you to them ๐
Funding for the RFC Editor function is currently provided by the Internet Society. ๐
๐ฆ where this page comes from
- ietf: rfc3117 2001-11
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms