RFC 2371

Transaction Internet Protocol Version 3.0

πŸ“… published July 1998, from ietf

RFC 2371: Transaction Internet Protocol Version 3.0 πŸŽ€

Bestie, this is the RFC for Transaction Internet Protocol Version 3.0 ✨

  • RFC number: 2371
  • Authors: J. Lyon (Microsoft), K. Evans and J. Klein (Tandem Computers) πŸ’–
  • Category: Standards Track πŸ”₯
  • Date: July 1998 πŸ“…
  • Obsoletes / Updates: the header doesn't list any RFCs that this one obsoletes or updates πŸ€·β€β™€οΈ

Status of This Memo

okay so this document is an Internet standards track protocol for the Internet community 🌸 It's asking for discussion and suggestions for improvements, so feel free to weigh in. For the standardization state and status of this protocol, go check the current edition of the "Internet Official Protocol Standards" (STD 1) πŸ“š And yes, distribution of this memo is unlimited πŸ¦‹

Copyright (C) The Internet Society (1998). All Rights Reserved. πŸ’…

Abstract

okay this one is literally the main idea πŸ’œ In lots of applications, different nodes work together on something, and sometimes it's super important that the work happens atomically. That means every node has to come to the same conclusion about whether the work gets finished, even if things break along the way 😭 This document proposes a simple, easy-to-implement protocol to make that happen ✨

Table of Contents

 1. Introduction                                                       2
 2. Example Usage                                                      3
 3. Transactions                                                       4
 4. Connections                                                        4
 5. Transaction Identifiers                                            5
 6. Pushing vs. Pulling Transactions                                   5
 7. TIP Transaction Manager Identification & Connection Establishment  6
 8. TIP Uniform Resource Locators                                      8
 9. States of a Connection                                            10
10. Protocol Versioning                                               12
11. Commands and Responses                                            12
12. Command Pipelining                                                13
13. TIP Commands                                                      13
14. Error Handling                                                    20
15. Connection Failure and Recovery                                   20
16. Security Considerations                                           22
17. References                                                        25
18. Authors' Addresses                                                26
19. Comments                                                          26
Appendix A. The TIP Multiplexing Protocol Version 2.0.               27
Fully Copyright Statement                                             31

1. Introduction 🧚

okay so the standard way to get atomic commitment is the two-phase commit protocol πŸ’… If you want the intro to atomic commitment and two-phase commit protocols, see [1] πŸ“–

Honestly, tons of two-phase commit protocols have been built over the years πŸ™ˆ But none of them caught on across the Internet, and the main reason is complexity. Most of that complexity comes from the fact that two-phase commit is bundled up with one specific program-to-program communication protocol, and that protocol sits on top of a really huge infrastructure 😩

So this memo proposes a very simple two-phase commit protocol πŸ“ Its trick is that it only specifies how different nodes agree on the outcome of a transaction. It allows (even requires) that the stuff the nodes are agreeing on gets communicated through other protocols 🫢 Because of that, we skip all the issues around application communication semantics and data representation, just to name a few πŸ’– And no matter what application communication protocol a transaction manager uses, it can use the Transport Layer Security protocol [3] to authenticate other transaction managers and encrypt messages πŸ”’

This protocol is mainly expected to be used for a transaction manager on one Internet node to talk to a transaction manager on another node ✨ It is possible to use it so application programs and/or resource managers can talk to transaction managers, but that's usually intra-node, and most transaction managers already have more-than-adequate interfaces for that job πŸ’…

We're not expecting this protocol to replace the existing ones πŸ¦‹ But we do expect it to be relatively easy for lots of existing heterogeneous transaction managers to implement, so they can talk to each other 🌈

More supplemental info about the TIP protocol can be found in [5] πŸ“š

2. Example Usage πŸ›’

okay the electronic shopping basket is basically the metaphor for online store-fronts πŸ›οΈ Customers browse an electronic catalog, pick goods, and drop them into their electronic shopping basket. HTTP servers [2] have various tricks, from URL encoding to context cookies, to keep track of client context (like a customer's shopping basket) and pick it back up on later requests πŸͺ

Once a customer is done shopping, they might decide to commit their selection and place the orders πŸ’œ Most orders have nothing to do with each other, except that they're all part of the same shopping transaction. Others might depend on each other, like if they were made as part of a special offer 🎁 No matter the details, a customer expects that all orders were successfully placed once they get a positive acknowledgment 😀

Here's the tea: today's store-fronts have to build their own special protocols to coordinate placing all those orders 😩 It gets especially messy when orders go through multiple store-fronts. That complexity limits how useful internet applications can be, and it slows growth πŸ“‰ So this protocol is meant to be a standard that lets Internet servers agree on a unit of shared work, like placing orders in a shopping basket 🫧

Here's how it plays out. The server placing the orders (say, a CGI program) might want to start a transaction by calling its local transaction manager, and then ask other servers doing part of the work to join the transaction πŸ™Œ The ordering server passes a reference to the transaction as user data on HTTP requests to the other servers. Those other servers call their own transaction managers to start a local transaction, then ask them to join the remote transaction using the protocol in this document πŸ”— Once all the orders are placed, the two-phase-commit protocol gets handed off to the transaction managers involved ✨ If the transaction commits, all orders were successfully placed and the customer gets a positive acknowledgment πŸŽ‰ If the transaction aborts, no orders get placed and the customer gets told something went wrong 😭

Transaction support makes these applications way easier to program, because exception handling and failure recovery get handed off to a special component πŸ‘‘ End users also don't get left dealing with the mess of only partial success πŸ’– This example is about HTTP servers, but applications can also use the protocol when they access a remote database (for example via ODBC), or invoke remote services using other existing protocols (for example RPC) 🧚 The protocol makes it easy for applications in a heterogeneous network to join the same transaction, even when they use different communication protocols 🌸

3. Transactions πŸŽ€

"Transaction" is the name for the programming model where computational work has atomic semantics πŸ’… In plain terms, either all the work completes successfully and the changes are made permanent (the transaction commits), or if any work fails, the changes are undone (the transaction aborts) 😀 The work that makes up a transaction (the unit of work) is defined by the application 🫢

4. Connections πŸ¦‹

The Transaction Internet Protocol (TIP) requires a reliable ordered stream transport with low connection setup costs πŸ’œ In an Internet (IP) environment, TIP runs over TCP. It can optionally use TLS to get a secured and authenticated connection πŸ”’ It can also optionally use a protocol to multiplex light-weight connections over that same TCP or TLS connection 🧩

Transaction managers that share transactions set up a TCP connection (and optionally a TLS connection) between them 🀝 The protocol uses a different connection for each simultaneous transaction shared between two transaction managers πŸ”€ Once a transaction has ended, that connection can be reused for a different transaction ♻️

Optionally, rather than tying a TCP or TLS connection to just one transaction, two transaction managers can agree on a protocol that multiplexes light-weight connections over the same TCP or TLS connection 🧚 Then each simultaneous transaction gets its own light-weight connection ✨ This cuts down on latency and resource consumption when running simultaneous transactions πŸ’¨ Lots of existing transaction processing systems use similar techniques. See Appendix A for an example of one such protocol πŸ“–

Note that even though the TIP protocol is only described in terms of TCP and TLS, nothing stops TIP from being used with other transport protocols 🌈 It's up to the implementor, though, to make sure the chosen transport gives equivalent semantics to TCP, and to map the TIP protocol onto it properly ⚠️

In this document, the terms "connection" or "TCP connection" can mean a TIP TCP connection, a TIP TLS connection, or a TIP multiplexing connection (over either TCP or TLS). It doesn't matter which one, because the behavior is the same in each case 🫢 Where the connection types do behave differently, the document says so explicitly πŸ’…

5. Transaction Identifiers πŸ“

Unfortunately, there's no single globally-accepted standard for the format of a transaction identifier πŸ™ƒ There are a bunch of standard and proprietary formats out there. The allowed formats for a TIP transaction identifier are described below, in the section "TIP Uniform Resource Locators" πŸ”— A transaction manager can map its internal transaction identifiers into this TIP format however it likes 🫧

Here's the other piece: in a superior/subordinate relationship, each party gets to assign its own identifier to the transaction πŸ€“ Those identifiers are exchanged when the relationship is first set up 🀝 So a transaction manager can use its own identifier format internally, but it must remember a foreign transaction identifier for each superior/subordinate relationship it's part of πŸ’œ

6. Pushing vs. Pulling Transactions πŸ’…

okay so picture this: a program on node "A" made a transaction and wants a program on node "B" to do some work as part of it 🫢 There are two classic ways that happens, called the "push" model and the "pull" model.

In the "push" model, the program on A first asks its transaction manager to export the transaction to node B. A's transaction manager sends a message to B's TM asking it to instantiate the transaction as a subordinate of A, and to return its name for the transaction. Then the program on A sends its counterpart on B a message along the lines of "Do some work, and make it part of the transaction that your transaction manager already knows of by the name ...". Because A's TM knows it sent the transaction to B's TM, A's TM knows to involve B's TM in the two-phase commit process ✨

In the "pull" model, the program on A just sends a message to B along the lines of "Do some work, and make it part of the transaction that my TM knows by the name ...". The program on B asks its TM to enlist in the transaction. At that point, B's TM will "pull" the transaction over from A. Because of this pull, A's TM knows to involve B's TM in the two-phase commit process πŸ¦‹

The protocol described here supports both the "push" and "pull" models πŸ‘‘


7. TIP Transaction Manager Identification and Connection Establishment πŸŽ€

okay, so for TIP transaction managers to connect, they must be able to identify and locate each other πŸ’– The information needed to do this is described by the TIP transaction manager address.

[This specification does not prescribe how TIP transaction managers initially obtain the transaction manager address (which will probably be via some implementation-specific configuration mechanism).]

TIP transaction manager addresses take the form:

  <hostport><path>

The <hostport> component comprises:

  <host>[:<port>]

where <host> is either a <dns name> or an <ip address>; and <port> is a decimal number specifying the port at which the transaction manager (or proxy) is listening for requests to establish TIP connections. If the port number is omitted, the standard TIP port number (3372) is used 🌸

A <dns name> is a standard name, acceptable to the domain name service. It must be sufficiently qualified to be useful to the receiver of the command.

An <ip address> is an IP address, in the usual form: four decimal numbers separated by period characters.

The <hostport> component defines the scope (locale) of the <path> component.

The <path> component of the transaction manager address contains data identifying the specific TIP transaction manager, at the location defined by <hostport>.

The <path> component takes the form:

    "/" [path_segments]

    path_segments = segment *( "/" segment )
    segment = *pchar *( ";" param )
    param = *pchar

    pchar = unreserved | escaped | ":" | "@" | "&" | "=" | "+"
    unreserved = ASCII character octets with values in the range

                 (inclusive): 48-57, 65-90, 97-122 | "$" | "-" | "_" |
                 "." | "!" | "~" | "*" | "'" | "(" | ")" | ","
    escaped = "%" hex hex
    hex = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" |
          "A" | "B" | "C" | "D" | "E" | "F" | "a" | "b" | "c" | "d" |
          "e" | "f"

The <path> component may consist of a sequence of path segments separated by a single slash "/" character. Within a path segment, the characters "/", ";", "=", and "?" are reserved. Each path segment may include a sequence of parameters, indicated by the semicolon ";" character. The parameters are not significant to the parsing of relative references 🧚

[It is intended that the form of the transaction manager address follow the proposed scheme for Uniform Resource Identifiers (URI) [8].]

So the TIP transaction manager address gives the connection initiator (the primary) the endpoint identifier to use for the TCP connection (<hostport>), and gives the connection receiver (the secondary) the path used to locate the specific TIP transaction manager (<path>). This is all the information required for the connection between the primary and secondary TIP transaction managers to be established πŸ’œ

After a connection has been established, the primary party issues an IDENTIFY command. This command includes as parameters two transaction manager addresses: the primary transaction manager address, and the secondary transaction manager address.

The primary transaction manager address identifies the TIP transaction manager that initiated the connection. This information is required in certain cases after connection failures, when one of the parties of the connection must re-establish a new connection to the other party in order to complete the two-phase-commit protocol. If the primary party needs to re-establish the connection, the job is easy: a connection is established in the same way as the original connection. However, if the secondary party needs to re-establish the connection, it must be known how to contact the initiator of the original connection. This information is supplied to the secondary via the primary transaction manager address on the IDENTIFY command. If a primary transaction manager address is not supplied, the primary party must not perform any action which would require a connection to be re-established (e.g. to perform recovery actions) πŸ™ˆ

The secondary transaction manager address identifies the receiving TIP transaction manager. In the case of TIP communication via intermediate proxy servers, this URL may be used by the proxy servers to correctly identify and connect to the required TIP transaction manager πŸ¦‹


8. TIP Uniform Resource Locators 🌈

Transactions and transaction managers are resources associated with the TIP protocol. Transaction managers and transactions are located using the transaction manager address scheme. Once a connection has been established, TIP commands may be sent to operate on transactions associated with the respective transaction managers πŸ“

Applications which want to pull a transaction from a remote node must supply a reference to the remote transaction which allows the local transaction manager (i.e. the transaction manager pulling the transaction) to connect to the remote transaction manager and identify the particular transaction. Applications which want to push a transaction to a remote node must supply a reference to the remote transaction manager (i.e. the transaction manager to which the transaction is to be pushed), which allows the local transaction manager to locate the remote transaction manager. The TIP protocol defines a URL scheme [4] which allows applications and transaction managers to exchange references to transaction managers and transactions πŸ”₯

A TIP URL takes the form:

  tip://<transaction manager address>?<transaction string>

where <transaction manager address> identifies the TIP transaction manager (as defined in Section 7 above); and <transaction string> specifies a transaction identifier, which may take one of two forms (standard or non-standard):

i. "urn:" <NID> ":" <NSS>

A standard transaction identifier, conforming to the proposed Internet Standard for Uniform Resource Names (URNs), as specified by RFC2141; where <NID> is the Namespace Identifier, and <NSS> is the Namespace Specific String. The Namespace ID determines the syntactic interpretation of the Namespace Specific String. The Namespace Specific String is a sequence of characters representing a transaction identifier (as defined by <NID>). The rules for the contents of these fields are specified by [6] (valid characters, encoding, etc.) πŸ¦‹

This format of <transaction string> may be used to express global transaction identifiers in terms of standard representations. Examples for <NID> might be <iso> or <xopen>. e.g.

  tip://123.123.123.123/?urn:xopen:xid

Note that Namespace Ids require registration. See [7] for details on how to do this ✨

ii. <transaction identifier>

A sequence of printable ASCII characters (octets with values in the range 32 through 126 inclusive (excluding ":")) representing a transaction identifier. In this non-standard case, it is the combination of <transaction manager address> and <transaction identifier> which ensures global uniqueness. e.g.

  tip://123.123.123.123/?transid1

To create a non-standard TIP URL from a transaction identifier, first replace any reserved characters in the transaction identifier with their equivalent escape sequences, then insert the appropriate transaction manager address. If the transaction identifier is one that you created, insert your own transaction manager address. If the transaction identifier is one that you received on a TIP connection that you initiated, use the secondary transaction manager address that was sent in the IDENTIFY command. If the transaction identifier is one that you received on a TIP connection that you did not initiate, use the primary transaction manager address that was received in the IDENTIFY command πŸ’…

TIP URLs must be guaranteed globally unique for all time. This uniqueness constraint ensures TIP URLs are never duplicated, thereby preventing possible non-deterministic behaviour. How uniqueness is achieved is implementation specific. For example, the Universally Unique Identifier (UUID, also known as a Globally Unique Identifier, or GUID (see [9])) could be used as part of the <transaction string>. Note also that some standard transaction identifiers may define their own rules for ensuring global uniqueness (e.g. OSI CCR atomic action identifiers) πŸ‘‘

Except as otherwise described above, the TIP URL scheme follows the rules for reserved characters as defined in [4], and uses escape sequences as defined in [4] Section 5 🌸

Note that the TIP protocol itself does not use the TIP URL scheme (it does use the transaction manager address scheme). The TIP URL scheme is proposed as a standard way to pass transaction identification information through other protocols, e.g. between cooperating application processes. The TIP URL may then be used to communicate to the local transaction manager the information necessary to associate the application with a particular TIP transaction, e.g. to PULL the transaction from a remote transaction manager. It is anticipated that each TIP implementation will provide some set of APIs for this purpose ([5] includes examples of such APIs) 🧚

9. States of a Connection ✨

Okay bestie, so at any given moment only ONE party on a connection is allowed to send commands πŸ™…β€β™€οΈ The other one can only reply to the commands it gets. In this doc, the one who's allowed to send commands is the primary, and the other is the secondary πŸ’œ At the start, whoever opened the connection is primary, but a few commands flip the roles. And the connection goes back to its original polarity whenever it hits Idle state πŸ”„

If multiplexing is happening, these same rules apply to each "virtual" connection separately, no matter what the underlying connection's polarity is (which will be in Multiplexing state) πŸ¦‹

Also, the secondary can send commands "out of band" using pipelining πŸ‘€ but that does NOT switch the roles, so the primary stays primary. See section 12 for the deets 🫢

Normally, only the primary should close a TIP connection, and only while it's in Initial state. It's generally not a vibe when the secondary closes it, though it may be needed in certain error cases πŸ’€

Every connection is in one of these states at any instant. From the secondary's point of view, the state changes when it sends a reply. From the primary's point of view, the state changes when it receives one 🌈

Initial

The connection starts out in Initial state 🌸 When it enters this state, the one who started the connection becomes primary, and the other becomes secondary. There's no transaction attached here. From Initial, the primary can send an IDENTIFY or a TLS command.

Idle

In Idle state, the primary and secondary have agreed on a protocol version, and the primary has given the secondary an identifier so it can reconnect if something fails πŸ” No transaction is attached. When the connection enters this state, the initiator becomes primary again and the other becomes secondary. From Idle, the primary can send any of these: BEGIN, MULTIPLEX, PUSH, PULL, QUERY and RECONNECT ✨

Begun

In Begun state, the connection has an active transaction, and that transaction can only be finished with a one-phase protocol πŸŽ€ A BEGUN response to a BEGIN command puts the connection here. If the connection fails while in Begun state, the transaction gets aborted, no exceptions 😭 From Begun, the primary can send ABORT or COMMIT.

Enlisted

In Enlisted state, the connection has an active transaction too, but this one can be finished with either a one-phase or a two-phase protocol πŸ’… A PUSHED response to a PUSH command, or a PULLED response to a PULL command, gets the connection here. If the connection fails in Enlisted state, the transaction aborts. From Enlisted, the primary can send ABORT, COMMIT, or PREPARE.

Prepared

In Prepared state, the connection is tied to a transaction that's been prepared 🧚 A PREPARED response to a PREPARE command, or a RECONNECTED response to a RECONNECT command, lands the connection here. Here's the plot twist: unlike the other states, if the connection fails in Prepared state, the transaction does NOT automatically abort πŸ‘‘ From Prepared, the primary can send ABORT or COMMIT.

Multiplexing

In Multiplexing state, the connection is being run by a multiplexing protocol, and that protocol brings its own set of connections πŸ¦‹ No TIP commands work on this connection itself. (But TIP commands totally work on the connections the multiplexing protocol hands out.) And the connection can never leave this state, so it's a one-way door πŸšͺ

Tls

In Tls state, the connection is being used by the TLS protocol, which gives it its own secured connection πŸ” Again, no TIP commands on this connection. (They DO work on the connection that TLS provides.) It can never leave this state either.

Error

In Error state, a protocol error happened and the connection is no longer useful πŸ’€ Once you're here, you can never leave. Sorry, not sorry.


10. Protocol Versioning πŸ’–

This doc describes version 3 of the protocol πŸ’œ To make room for future versions, the primary sends a message with the lowest and the highest version numbers it understands. The secondary replies with the highest version number IT understands 🀝

After that handshake, the two of them talk using the smaller of the highest version numbers, meaning the highest version that BOTH of them get πŸ™Œ This exchange is mandatory, and it happens through the IDENTIFY command (and its IDENTIFIED response). No skipping it, periodt ✨

If one party's highest version is considered obsolete and the other party no longer supports it, no useful communication can happen. In that case, the newer party should just drop the connection πŸ₯€


11. Commands and Responses πŸŽ€

Every command and response is one line of ASCII text, using only octets with values from 32 through 126 (inclusive) πŸ”€ Each line ends with either a CR (an octet with value 13) or an LR (an octet with value 10). A line can be broken into one or more "words," and words are separated by one or more space octets (value 32) πŸ“

Spaces at the start and/or end of a line are allowed, and they get ignored πŸ™ˆ

Lines that are empty, or that are only spaces, also get ignored. (So yes, you can end your lines with both a CR and an LF if you want! The LF just counts as ending an empty line, and it gets tossed πŸ’…)

The first word of every line tells you what kind of command or response it is, and ALL the defined commands and responses use only uppercase letters πŸ” 

Some commands and responses have extra words that carry parameters. Each command and response takes a fixed number of parameters πŸ“

And here's a lowkey useful bit: any words on a line after (and including) the first undefined word are totally ignored. These are basically a sticky note for human-readable stuff, like debugging info πŸ“


12. Command Pipelining πŸ¦‹

To cut down on latency and make things more efficient, you can pipeline multiple TIP "lines" (commands or responses) by smooshing them together and sending them as one single message πŸš€ Lines can also be sent "ahead," like when the secondary sends something early for the primary to process later. Two examples: an ABORT command right before a BEGIN command, or a COMMITTED response right before a PULL command 🫢

Sending pipelined lines is an implementation option, and so is deciding which lines get pipelined together πŸ€·β€β™€οΈ Generally, the sender must be certain that the pipelined line will be valid for the connection's state at the moment the receiver processes it. Making sure of that is the sender's job, not anyone else's 🧠

Every implementation must support RECEIVING pipelined lines. Here's how that works:

When the connection is in a state where a line should be read (either a command or a response), that line gets processed when it arrives. No more lines are read from the connection until processing gets back to a state like that. If a line shows up when it isn't the other party's turn to send, that line is NOT rejected πŸ™…β€β™€οΈ Instead, it gets held and processed once the connection state needs it. The receiving party must process lines and send responses in the same order the lines were received. And if a line causes an error, the connection enters the Error state, and every line after it on that connection gets thrown away πŸ—‘οΈ

13. TIP Commands πŸ‘‘

Commands are about either connections or transactions 🧩 The connection commands are IDENTIFY, MULTIPLEX and TLS. The transaction commands are ABORT, BEGIN, COMMIT, PREPARE, PULL, PUSH, QUERY, and RECONNECT.

Here's the full list of valid commands, plus every possible response to each one πŸ“œ

ABORT πŸ’…

ABORT is only valid in Begun, Enlisted, and Prepared states. It tells the secondary that the connection's current transaction is going to abort. The possible responses are:

  • ABORTED: The transaction has aborted, and the connection enters Idle state.
  • ERROR: The command was sent in the wrong state, or it was malformed. The connection enters the Error state.

BEGIN 🌸

BEGIN is only valid in Idle state. It asks the secondary to create a new transaction and link it to the connection. The new transaction will be finished with a one-phase protocol ✨ The possible responses are:

  • BEGUN <transaction identifier>: A new transaction began successfully, and it's now the connection's current transaction. The connection enters Begun state.
  • NOTBEGUN: A new transaction couldn't be started. The connection stays in Idle state.
  • ERROR: The command was sent in the wrong state, or it was malformed. The connection enters the Error state.

COMMIT πŸŽ€

COMMIT is valid in Begun, Enlisted or Prepared states. In Begun or Enlisted state, it asks the secondary to try committing the transaction. In Prepared state, it tells the secondary that the transaction has committed πŸ₯³

Heads up: in Enlisted state, this command is a one-phase protocol, and it should ONLY be used when the sender has 1) no local recoverable resources in the transaction, and 2) only one subordinate (so the sender won't be involved in any transaction recovery process). Main character energy, but with rules πŸ“‹

The possible responses are:

  • ABORTED: This response is only possible from the Begun and Enlisted states. It means some party vetoed the commit, so the transaction got aborted instead. The connection enters Idle state.
  • COMMITTED: The transaction has committed, and the primary no longer owes the secondary anything for it. The connection enters Idle state πŸŽ‰
  • ERROR: The command was sent in the wrong state, or it was malformed. The connection enters the Error state.

ERROR πŸ™ˆ

ERROR is valid in ANY state. It tells the secondary that a previous response wasn't recognized or was badly formed πŸ’€ A secondary should not respond to this command. The connection enters Error state.

IDENTIFY πŸ”‘

IDENTIFY  <lowest protocol version>
          <highest protocol version>
          <primary transaction manager address> | "-"
          <secondary transaction manager address>

Okay bestie, IDENTIFY is only a thing in the Initial state πŸŽ€, so don't try it anywhere else, it's giving rude. The primary party tells the secondary party three things ✨: 1) the lowest and highest protocol version it supports (all versions between the lowest and highest must be supported; 2) optionally, an identifier for the primary party so the secondary party can re-establish a connection later if it ever needs to (the primary transaction manager address); and 3) an identifier that intermediate proxy servers can use to reach the required TIP transaction manager (the secondary transaction manager address). If there's no primary transaction manager address, the secondary party will answer any PREPARE commands with ABORTED or READONLY πŸ’€. The possible responses are:

  • IDENTIFIED <protocol version>
    • The secondary party got contacted successfully and saved the primary transaction manager address πŸ¦‹. The response has the highest protocol version the secondary party supports. All future communication is assumed to happen using the smaller of the protocol versions in the IDENTIFY command and the IDENTIFIED response πŸ’œ. The connection enters the Idle state.
  • NEEDTLS
    • The secondary party is only willing to talk over TLS secured connections πŸ”’. The connection enters the Tls state, and all later communication follows the TLS protocol. The TLS protocol begins with the first octet after the line terminator of the IDENTIFY command (for data sent by the primary party), and the first byte after the line terminator of the NEEDTLS response (for data sent by the secondary party). This means an implementation must not send both a CR and a LF octet after either the IDENTIFY command or the NEEDTLS response, lest the LF octet be mistaken for the first byte of the TLS protocol 😭. The connection provided by the TLS protocol starts out in the Initial state. After TLS has been negotiated, the primary party must resend the IDENTIFY command. If the primary party cannot support (or refuses to use) the TLS protocol, it closes the connection.
  • ERROR
    • The command was issued in the wrong state, or was malformed. This response also shows up if the secondary party doesn't support any version of the protocol in the range the primary party supports. The connection enters the Error state. The primary party should close the connection.

MULTIPLEX <protocol-identifier>

This command is only valid in the Idle state πŸ¦‹. It's basically asking if you two can share the connection with a multiplexing protocol so a huge number of connections can run over the existing one 🧚. The primary suggests a particular multiplexing protocol, and the secondary party can either accept or reject it.

Right now, the only defined protocol identifier is "TMP2.0", which refers to the TIP Multiplexing Protocol, version 2.0. See Appendix A for the details πŸ“–. Other protocol identifiers may be defined in the future.

If the MULTIPLEX command is accepted, the multiplexing protocol totally takes over the underlying connection πŸ‘‘. It begins with the first octet after the line terminator of the MULTIPLEX command (for data sent by the initiator), and the first byte after the line terminator of the MULTIPLEXING response (for data received by the initiator). This means an implementation must not send both a CR and a LF octet after either the MULTIPLEX command or the MULTIPLEXING response, lest the LF octet be mistaken for the first byte of the multiplexing protocol πŸ™ˆ.

Note that when using TMP V2.0, a single TIP command (TMP application message) must be wholly contained within a single TMP packet. The TMP PUSH flag is not used by TIP ✨. The possible responses to the MULTIPLEX command are:

  • MULTIPLEXING
    • The secondary party agrees to use the specified multiplexing protocol 🫢. The connection enters the Multiplexing state, and all later communication follows that protocol. All connections created by the multiplexing protocol start out in the Idle state.
  • CANTMULTIPLEX
    • The secondary party can't support (or refuses to use) the specified multiplexing protocol. The connection remains in the Idle state.
  • ERROR
    • The command was issued in the wrong state, or was malformed. The connection enters the Error state.

PREPARE

This command is valid only in the Enlisted state πŸ’…. It asks the secondary to prepare the transaction for commitment (phase one of two-phase commit). The possible responses are:

  • PREPARED
    • The subordinate has prepared the transaction, and the connection enters the PREPARED state 🌈.
  • ABORTED
    • The subordinate has vetoed committing the transaction 🚫. The connection enters the Idle state. After this response, the superior has no responsibilities to the subordinate with respect to the transaction.
  • READONLY
    • The subordinate no longer cares whether the transaction commits or aborts πŸ“. The connection enters the Idle state. After this response, the superior has no responsibilities to the subordinate with respect to the transaction.
  • ERROR
    • The command was issued in the wrong state, or was malformed. The connection enters the Error state.

PULL <superior's transaction identifier> <subordinate's transaction identifier>

This command is only valid in the Idle state 🌸. It tries to set up a superior/subordinate relationship in a transaction, with the primary party of the connection as the subordinate (i.e., he is pulling a transaction from the secondary party). Note that the entire value of <transaction string> (as defined in the section "TIP Uniform Resource Locators") must be specified as the transaction identifier πŸ”—. The possible responses are:

  • PULLED
    • The relationship has been established! πŸŽ‰ Once the primary gets this response, the specified transaction becomes the current transaction of the connection, and the connection enters the Enlisted state. The roles of primary and secondary also flip (the superior becomes the primary for the connection).
  • NOTPULLED
    • The relationship has not been established, possibly because the secondary party no longer has the requested transaction πŸ˜”. The connection remains in the Idle state.
  • ERROR
    • The command was issued in the wrong state, or was malformed. The connection enters the Error state.

PUSH <superior's transaction identifier>

This command is valid only in the Idle state πŸŽ€. It tries to set up a superior/subordinate relationship in a transaction with the primary as the superior πŸ‘‘. Note that the entire value of <transaction string> (as defined in the section "TIP Uniform Resource Locators") must be specified as the transaction identifier. The possible responses are:

  • PUSHED <subordinate's transaction identifier>
    • The relationship has been established, and the subordinate hands back the identifier it uses for the transaction ✨. The transaction becomes the current transaction for the connection, and the connection enters the Enlisted state.
  • ALREADYPUSHED <subordinate's transaction identifier>
    • The relationship has been established, and the subordinate hands back its identifier for the transaction. However, the subordinate already knows about the transaction and is expecting the two-phase commit protocol to show up over a different connection πŸ™ƒ. In this case, the connection remains in the Idle state.
  • NOTPUSHED
    • The relationship couldn't be established πŸ’”. The connection remains in the Idle state.
  • ERROR
    • The command was issued in the wrong state, or was malformed. The connection enters the Error state.

QUERY <superior's transaction identifier>

This command is valid only in the Idle state 🧚. A subordinate uses it to check whether a specific transaction still exists at the superior πŸ”. The possible responses are:

  • QUERIEDEXISTS
    • The transaction still exists πŸ’–. The connection remains in the Idle state.
  • QUERIEDNOTFOUND
    • The transaction is gone πŸ₯€. The connection remains in the Idle state.
  • ERROR
    • The command was issued in the wrong state, or was malformed. The connection enters the Error state.

RECONNECT <subordinate's transaction identifier>

This command is valid only in the Idle state πŸ”. A superior uses it to re-establish a connection for a transaction, when the previous connection was lost during the Prepared state πŸ’«. The possible responses are:

  • RECONNECTED
    • The subordinate accepts the reconnection 🫢. The connection enters the Prepared state.
  • NOTRECONNECTED
    • The subordinate no longer knows about the transaction. The connection remains in the Idle state.
  • ERROR
    • The command was issued in the wrong state, or was malformed. The connection enters the Error state.

TLS

This command is valid only in the Initial state πŸ”’. A primary uses it to try to set up a secured connection using TLS.

If the TLS command is accepted, the TLS protocol totally takes over the underlying connection πŸ’…. It begins with the first octet after the line terminator of the TLS command (for data sent by the primary), and the first byte after the line terminator of the TLSING response (for data received by the primary). This means an implementation must not send both a CR and a LF octet after either the TLS command or the TLSING response, lest the LF octet be mistaken for the first byte of the TLS protocol πŸ™ˆ.

The possible responses to the TLS command are:

  • TLSING
    • The secondary party agrees to use the TLS protocol [3] πŸ”. The connection enters the Tls state, and all later communication follows the TLS protocol. The connection provided by the TLS protocol starts out in the Initial state.
  • CANTTLS
    • The secondary party can't support (or refuses to use) the TLS protocol. The connection remains in the Initial state.
  • ERROR
    • The command was issued in the wrong state, or was malformed. The connection enters the Error state.

14. Error Handling ✨

If either party gets a line it can't understand, it closes the connection πŸšͺ. If either party (whether it's a command or a response) receives an ERROR indication or an ERROR response on a connection, the connection enters the Error state, and no further communication is possible on that connection πŸ’€. An implementation may decide to close the connection. The other party treats closing the connection as a communication failure.

Getting an ERROR indication or an ERROR response means the other party believes you haven't properly implemented the protocol 😭🫢.

15. Connection Failure and Recovery πŸ’”βœ¨

Okay so a connection can fail because of a communication failure, or because either party closes it 🫠. It's assumed TIP implementations will use some private mechanism to notice when a connection has failed (e.g. socket keepalive, or a timeout scheme) 🫢.

Depending on the state of a connection, transaction managers will need to take different actions when it fails πŸ¦‹.

If the connection fails in Initial or Idle state, it doesn't refer to a transaction, so no action is necessary 🌸.

If the connection fails in the Multiplexing state, every connection provided by the multiplexing protocol is assumed to have failed 😭. Each one gets treated independently 🧚.

If the connection fails in Begun or Enlisted state and COMMIT has been sent, then transaction completion has been delegated to the subordinate (the superior is not involved) πŸ‘‘. So the outcome is unknown to the superior, but it's known at the subordinate. The superior uses application-specific means to figure out the outcome (note that transaction integrity is not compromised in this case, since the superior has no recoverable resources involved in the transaction) πŸ”₯. If the connection fails in Begun or Enlisted state and COMMIT has not been sent, the transaction will be aborted πŸ’€.

If the connection fails in Prepared state, the right move is different for the superior and the subordinate in the transaction 🌈.

If the superior determines that the transaction commits, then it MUST eventually establish a new connection to the subordinate, and send a RECONNECT command for the transaction. If it receives a NOTRECONNECTED response, it need do nothing else. But if it receives a RECONNECTED response, it MUST send a COMMIT request and receive a COMMITTED response πŸŽ€.

If the superior determines that the transaction aborts, it is allowed to (but not REQUIRED to) establish a new connection and send a RECONNECT command for the transaction. If it receives a RECONNECTED response, it SHOULD send an ABORT command πŸ™…β€β™€οΈ.

The above definition allows the superior to reestablish the connection before it knows the outcome of the transaction, if it finds that convenient πŸ’…. Having succeeded in a RECONNECT command, the connection is back in Prepared state, and the superior can send a COMMIT or ABORT command as appropriate once it knows the outcome πŸ¦‹.

Note that it's possible for a RECONNECT command to be received by the subordinate before it's aware that the previous connection has failed πŸ‘€. In this case, the subordinate treats the RECONNECT command as a failure indication, cleans up any resources associated with the connection, and associates the transaction state with the new connection ✨.

If a subordinate notices a connection failure in Prepared state, it SHOULD periodically attempt to create a new connection to the superior and send a QUERY command for the transaction. It SHOULD continue doing this until one of the following two events occurs πŸ”:

  1. It receives a QUERIEDNOTFOUND response from the superior. In this case, the subordinate SHOULD abort the transaction πŸ™ƒ.
  2. The superior, on some connection that it initiated, sends a RECONNECT command for the transaction to the subordinate. In this case, the subordinate can expect to learn the outcome of the transaction on this new connection πŸ₯Ή. If this new connection should fail before the subordinate learns the outcome of the transaction, it SHOULD again start sending QUERY commands πŸ”„.

Note that if a TIP system receives either a QUERY or a RECONNECT command, and for some reason is unable to satisfy the request (e.g. the necessary recovery information is not currently available), then the connection SHOULD be dropped 🚫.


16. Security Considerations πŸ›‘οΈ

This section is meant to inform application developers, transaction manager developers, and users about the security implications of TIP as described by this document πŸŽ€. It doesn't include definitive solutions to the issues described, though it does make some suggestions for reducing security risks πŸ’œ.

As with all two phase-commit protocols, any security mechanisms applied to the application communication protocol are liable to be subverted unless corresponding mechanisms are applied to the commitment protocol. For example, any authentication between the parties using the application protocol MUST be supported by security of the TIP exchanges to at least the same level of certainty 😀.

16.1. TLS, Mutual Authentication and Authorization πŸ”

TLS provides optional client-side authentication, optional server-side authentication, and optional packet encryption 🌸.

A TIP implementation MAY refuse to provide service unless TLS is being used. It MAY refuse to provide service if packet encryption is not being used. It MAY refuse to provide service unless the remote party has been authenticated (via TLS) πŸ™…β€β™€οΈ.

A TIP implementation SHOULD be willing to be authenticated itself (via TLS). This is true regardless of whether the implementation is acting as a client or a server 🫢.

Once a remote party has been authenticated, a TIP transaction manager MAY use that remote party's identity to decide what operations to allow πŸ‘‘.

Whether TLS is to be used on a connection, and if so, how TLS is to be used, and what operations are to subsequently be allowed, is determined by the security policies of the connecting TIP transaction managers towards each other. How these security policies are defined, and how a TIP transaction manager learns of them, is totally private to the implementation and beyond the scope of this document 🀐.

16.2. PULL-Based Denial-of-Service Attack πŸ™ˆ

Assume that a malicious user knows the identity of a transaction that is currently active in some transaction manager. If the malefactor opens a TIP connection to the transaction manager, sends a PULL command, then closes the connection, he can cause that transaction to be aborted. This results in a denial of service to the legitimate owner of the transaction πŸ’€.

An implementation MAY avoid this attack by refusing PULL commands unless TLS is being used, the remote party has been authenticated, and the remote party is trusted πŸ›‘οΈ.

16.3. PUSH-Based Denial-of-Service Attack 🚨

When the connection between two transaction managers is closed while a transaction is in the Prepared state, each transaction manager needs to remember information about the transaction until a connection can be re-established 🧠.

If a malicious user exploits this fact to repeatedly create transactions, get them into Prepared state and drop the connection, he may cause a transaction manager to suffer resource exhaustion, thus denying service to all legitimate users of that transaction manager 😭.

An implementation MAY avoid this attack by refusing PUSH commands unless TLS is being used, the remote party has been authenticated, and the remote party is trusted πŸ›‘οΈ.

16.4. Transaction Corruption Attack 🦹

If a subordinate transaction manager has lost its connection for a particular prepared transaction, a malicious user can initiate a TIP connection to the transaction manager, and send it a RECONNECT command followed by either a COMMIT or an ABORT command for the transaction. The malicious user could thus cause part of a transaction to be committed when it should have been aborted, or vice versa πŸ™€.

An implementation MAY avoid this attack by recording the authenticated identity of its superior in a transaction, and by refusing RECONNECT commands unless TLS is being used and the authenticated identity of the remote party is the same as the identity of the original superior πŸ”’.

16.5. Packet-Sniffing Attacks πŸ‘€

If a malicious user can intercept traffic on a TIP connection, he may be able to deduce information useful in planning other attacks. For example, if comment fields include the product name and version number of a transaction manager, a malicious user might be able to use this information to determine what security bugs exist in the implementation πŸ•΅οΈβ€β™€οΈ.

An implementation MAY avoid this attack by always using TLS to provide session encryption, and by not putting any personalizing information on the TLS/TLSING command/response pair 🫣.

16.6. Man-in-the-Middle Attack 😈

If a malicious user can intercept and alter traffic on a TIP connection, he can wreak havoc in a number of ways. For example, he could replace a COMMIT command with an ABORT command πŸ”₯.

An implementation MAY avoid this attack by always using TLS to provide session encryption and authentication of the remote party πŸ”.


17. References πŸ“š

[1] Gray, J. and A. Reuter (1993), Transaction Processing: Concepts and Techniques. San Francisco, CA: Morgan Kaufmann Publishers. (ISBN 1-55860-190-2).

[2] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., and T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2068, January 1997.

[3] Dierks, T., et. al., "The TLS Protocol Version 1.0", Work in Progress.

[4] Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform Resource Locators (URL)", RFC 1738, December 1994.

[5] Evans, K., Klein, J., and J. Lyon, "Transaction Internet Protocol - Requirements and Supplemental Information", RFC 2372, July 1998.

[6] Moats, R., "URN Syntax", RFC 2141, May 1997.

[7] Faltstrom, P., et. al., "Namespace Identifier Requirements for URN Services", Work in Progress.

[8] Berners-Lee, T., et. at., "Uniform Resource Identifiers (URI): Generic Syntax and Semantics", Work in Progress.

[9] Leach, P., and R. Salz, "UUIDs and GUIDs", Work in Progress.


18. Authors' Addresses πŸ–‹οΈ

Jim Lyon
Microsoft Corporation
One Microsoft Way
Redmond, WA  98052-6399, USA

Phone: +1 (206) 936 0867
Fax:   +1 (206) 936 7329
EMail: JimLyon@Microsoft.Com


Keith Evans
Tandem Computers, Inc.
5425 Stevens Creek Blvd
Santa Clara, CA 95051-7200, USA

Phone: +1 (408) 285 5314
Fax:   +1 (408) 285 5245
EMail: Keith.Evans@Tandem.Com


Johannes Klein
Tandem Computers Inc.
10555 Ridgeview Court
Cupertino, CA 95014-0789, USA

Phone: +1 (408) 285 0453
Fax:   +1 (408) 285 9818
EMail: Johannes.Klein@Tandem.Com

19. Comments πŸ’Œ

Please send comments on this document to the authors at <JimLyon@Microsoft.Com>, <Keith.Evans@Tandem.Com>, <Johannes.Klein@Tandem.Com>, or to the TIP mailing list at <Tip@Lists.Tandem.Com>. You can subscribe to the TIP mailing list by sending mail to <Listserv@Lists.Tandem.Com> with the line "subscribe tip <full name>" somewhere in the body of the message πŸ“¬.


Appendix A. The TIP Multiplexing Protocol Version 2.0. 🧩

This appendix describes version 2.0 of the TIP Multiplexing Protocol (TMP). TMP is intended solely for use with the TIP protocol, and forms part of the TIP protocol specification (although its implementation is OPTIONAL). TMP V2.0 is the only multiplexing protocol supported by TIP V3.0 ✨.

Abstract 🌷

TMP provides a simple mechanism for creating multiple lightweight connections over a single TCP connection. Several such lightweight connections can be active simultaneously. TMP provides a byte oriented service, but allows message boundaries to be marked πŸ¦‹.

A.1. Introduction ✨

Okay so picture this, bestie πŸ’– A bunch of protocols on the Internet open a brand new TCP connection for every single transaction 😭. Those transactions are super short-lived, so setting up and tearing down all those TCP connections gets expensive, both in terms of resources and in the delays from TCP's congestion control mechanisms πŸ™ˆ.

Enter the TIP Multiplexing Protocol (TMP) πŸ¦‹, a simple protocol that runs on top of TCP and lets you create multiple lightweight connections over a single transport connection. So TMP makes more efficient use of TCP connections. Data from several different TMP connections can be interleaved, and both message boundaries and end of stream markers can be provided πŸŽ€.

TMP runs on a reliable, byte-ordered transport service, so it can skip most of the extra work TCP has to do to ensure reliability πŸ’…. TMP connections don't need to be confirmed, so there's no waiting for a handshake to finish before data can be sent. Main character energy πŸ‘‘.

Note: TMP is not intended as a generalized multiplexing protocol. If you are designing a different protocol that needs multiplexing, TMP may or may not be appropriate. Protocols with large messages can exceed the buffering capabilities of the receiver, and under certain conditions this can cause deadlock πŸ’€. TMP when used with TIP does not suffer from this problem since TIP is a request-response protocol, and all messages are short ✨.

A.2. Protocol Model 🌊

The basic model is multiple lightweight connections operating over a reliable stream of bytes. The party that initiated the connection is called the primary, and the party that accepted it is called the secondary πŸ‘‘.

Connections can be unidirectional or bi-directional. For a bi-directional connection, each end can be closed separately. Connections can be closed normally, or reset to signal an abortive release. Aborting a connection closes both data streams πŸ’”.

Once a connection is open, applications can send messages over it and signal the end of application-level messages πŸ’Œ. Application messages are encapsulated in TMP packets and carried over the byte stream. A single TIP command (TMP application message) must be wholly contained within a single TMP packet πŸŽ€.

A.3. TMP Packet Format πŸ“¦

A TMP packet is a 64 bit header followed by zero or more octets of data. The header has three fields: a flag byte, the connection identifier, and the packet length. Both integers, the connection identifier and the packet length, must be sent in network byte order πŸ”’.

    FLAGS
   +--------+--------+--------+--------+
   |SFPR0000| Connection ID            |
   +--------+--------+--------+--------+
   |        | Length                   |
   +--------+--------+--------+--------+

A.3.1. Flag Details 🚩

   +-------+-----------+-----------------------------------------+
   | Name  | Mask      | Description                             |
   +-------+-----------+ ----------------------------------------+
   | SYN   | 1xxx|0000 | Open a new connection                   |
   | FIN   | x1xx|0000 | Close an existing connection            |
   | PUSH  | xx1x|0000 | Mark application level message boundary |
   | RESET | xxx1|0000 | Abort the connection                    |
   +-------+-----------+-----------------------------------------+

A.4. Connection Identifiers πŸ†”

Each TMP connection is identified by a 24 bit integer. Connections created by the party that initiated the underlying TCP connection must have even identifiers. Connections created by the other party must have odd identifiers 🧚.

A.5. TMP Connection States 🌈

TMP connections can exist in several different states: Closed, OpenWrite, OpenSynRead, OpenSynReset, OpenReadWrite, CloseWrite, and CloseRead. A connection can change its state in response to receiving a packet with the SYN, FIN, or RESET bits set, or in response to an API call by the application. The available API calls are open, close, and abort πŸ”„.

The meaning of most states is obvious. For example, OpenWrite means a connection has been opened for writing πŸ’…. The states OpenSynRead and OpenResetRead need a bit more explanation πŸ€”.

In the OpenSynRead state, a primary opened and immediately closed the output data stream of a connection. It is now waiting for a SYN response from the secondary to open the input data stream for reading πŸ“–.

In the OpenResetRead state, a primary opened and immediately aborted a connection. It is now waiting for a SYN response from the secondary to finally close the connection 😭.

A.6. Event Priorities and State Transitions 🚦

The state table below describes the actions and state transitions that occur in response to a given event. The events accepted by each state are listed in priority order, with the highest priority first. If multiple events are present in a message, the events matching the list are processed. If multiple events match, the event with the highest priority is accepted and processed first. Any remaining events are processed in the resultant successor state 🧠.

For example, say a TMP connection at the secondary is in the Closed state, and the secondary receives a packet containing a SYN event, a FIN event, and an input data event (i.e. DATA-IN). The secondary first accepts the SYN event, because it is the only match in the Closed state. It accepts the connection, sends a SYN event, and enters the ReadWrite state. The SYN event is removed from the list of pending events, leaving FIN and DATA-IN. In the ReadWrite state, the secondary reads the input data first, because DATA-IN has higher priority than FIN. Once the data has been read and DATA-IN has been removed from the list of pending events, the FIN event is processed and the secondary enters the CloseWrite state 🎬.

If the secondary receives a packet containing a SYN event, and is for some reason unable to accept the connection (e.g. insufficient resources), it should reject the request by sending a SYN event followed by a RESET event. Both events can be sent as part of the same TMP packet πŸ™…β€β™€οΈ.

If either party receives a TMP packet that it does not understand, or an event in an incorrect state, it closes the TCP connection 🚨.

   +==============+=========+==========+==============+
   | Entry State  | Event   | Action   | Exit State   |
   +==============+=========+==========+==============+
   | Closed       | SYN     | SYN      | ReadWrite    |
   |              | OPEN    | SYN      | OpenWrite    |
   +--------------+---------+----------+--------------+
   | OpenWrite    | SYN     | Accept   | ReadWrite    |
   |              | WRITE   | DATA-OUT | OpenWrite    |
   |              | CLOSE   | FIN      | OpenSynRead  |
   |              | ABORT   | RESET    | OpenSynReset |
   +--------------+---------+----------+--------------+
   | OpenSynRead  | SYN     | Accept   | CloseRead    |
   +--------------+---------+----------+--------------+
   | OpenSynReset | SYN     | Accept   | Closed       |
   +--------------+---------+----------+--------------+
   | ReadWrite    | DATA-IN | Accept   | ReadWrite    |
   |              | FIN     | Accept   | CloseWrite   |
   |              | RESET   | Accept   | Closed       |
   |              | WRITE   | DATA-OUT | ReadWrite    |
   |              | CLOSE   | FIN      | CloseRead    |
   |              | ABORT   | RESET    | Closed       |
   +--------------+---------+----------+--------------+
   | CloseWrite   | RESET   | Accept   | Closed       |
   |              | WRITE   | DATA-OUT | CloseWrite   |
   |              | CLOSE   | FIN      | Closed       |
   |              | ABORT   | RESET    | Closed       |
   +--------------+---------+----------+--------------+
   | CloseRead    | DATA-IN | Accept   | CloseRead    |
   |              | FIN     | Accept   | Closed       |
   |              | RESET   | Accept   | Closed       |
   |              | ABORT   | RESET    | Closed       |
   +--------------+---------+----------+--------------+

TMP Event Priorities and State Transitions

Here's the legal bit, kept exactly as written πŸ“œ:

Copyright (C) The Internet Society (1998). 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.

πŸ“¦ where this page comes from