SignalR 289 B

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. commit 18f308fb05eabcc72ebc680a5c21c04b961a387e
  2. Author: Pawel Kadluczka <[email protected]>
  3. Date: Fri Nov 10 15:57:19 2017 -0800
  4. Updating spec with StreamInvocation, removing StreamCompletion
  5. diff --git a/specs/HubProtocol.md b/specs/HubProtocol.md
  6. index 09f1ab0ec8c..c0f76c410d1 100644
  7. --- a/specs/HubProtocol.md
  8. +++ b/specs/HubProtocol.md
  9. @@ -4,9 +4,9 @@ The SignalR Protocol is a protocol for two-way RPC over any Message-based transp
  10. ## Terms
  11. -* Caller - The node that is issuing an `Negotiation`, `Invocation`, `CancelInvocation` messages and receiving `Completion`, `StreamItem` and `StreamCompletion` messages (a node can be both Caller and Callee for different invocations simultaneously)
  12. -* Callee - The node that is receiving an `Negotiation`, `Invocation`, `CancelInvocation` messages and issuing `Completion`, `StreamItem` and `StreamCompletion` messages (a node can be both Callee and Caller for different invocations simultaneously)
  13. -* Binder - The component on each node that handles mapping `Invocation` messages to method calls and return values to `Completion`, `StreamItem` and `StreamCompletion` messages
  14. +* Caller - The node that is issuing an `Negotiation`, `Invocation`, `StreamInvocation`, `CancelInvocation` messages and receiving `Completion` and `StreamItem` messages (a node can be both Caller and Callee for different invocations simultaneously)
  15. +* Callee - The node that is receiving an `Negotiation`, `Invocation`, `StreamInvocation`, `CancelInvocation` messages and issuing `Completion` and `StreamItem` messages (a node can be both Callee and Caller for different invocations simultaneously)
  16. +* Binder - The component on each node that handles mapping `Invocation` and `StreamInvocation` messages to method calls and return values to `Completion` and `StreamItem` messages
  17. ## Transport Requirements
  18. @@ -22,9 +22,9 @@ In the SignalR protocol, the following types of messages can be sent:
  19. * `Negotiation` Message - Sent by the client to negotiate the message format.
  20. * `Invocation` Message - Indicates a request to invoke a particular method (the Target) with provided Arguments on the remote endpoint.
  21. +* `StreamInvocation` Message - Indicates a request to invoke a streaming method (the Target) with provided Arguments on the remote endpoint.
  22. * `StreamItem` Message - Indicates individual items of streamed response data from a previous Invocation message.
  23. -* `Completion` Message - Indicates a previous Invocation has completed. Contains an error if the invocation concluded with an error, or the result of method invocation. The result will be absent for `void` methods.
  24. -* `StreamCompletion` Message - Indicates a previous Invocation has completed, and no further `StreamItem` messages will be received. Contains an error if the invocation concluded with an error.
  25. +* `Completion` Message - Indicates a previous Invocation or StreamInvocation has completed. Contains an error if the invocation concluded with an error or the result of a non-streaming method invocation. The result will be absent for `void` methods. In case of streaming invocations no further `StreamItem` messages will be received
  26. * `CancelInvocation` Message - Sent by the client to cancel a streaming invocation on the server.
  27. After opening a connection to the server the client must send a `Negotiation` message to the server as its first message. The negotiation message is **always** a JSON message and contains the name of the format (protocol) that will be used for the duration of the connection. If the server does not support the protocol requested by the client or the first message received from the client is not a `Negotiation` message the server must close the connection.
  28. @@ -54,10 +54,10 @@ There are three kinds of interactions between the Caller and the Callee:
  29. In order to perform a single invocation, the Caller follows the following basic flow:
  30. 1. Allocate a unique `Invocation ID` value (arbitrary string, chosen by the Caller) to represent the invocation
  31. -2. Send an `Invocation` message containing the `Invocation ID`, the name of the `Target` being invoked, and the `Arguments` to provide to the method.
  32. +2. Send an `Invocation` or `StreamingInvocation` message containing the `Invocation ID`, the name of the `Target` being invoked, and the `Arguments` to provide to the method.
  33. 3. If the `Invocation` is marked as non-blocking (see "Non-Blocking Invocations" below), stop here and immediately yield back to the application.
  34. -4. Wait for a `StreamItem`, `Completion` or `StreamCompletion` message with a matching `Invocation ID`
  35. -5. If a `Completion` or `StreamCompletion` message arrives, go to 8
  36. +4. Wait for a `StreamItem` or `Completion` message with a matching `Invocation ID`
  37. +5. If a `Completion` message arrives, go to 8
  38. 6. If the `StreamItem` message has a payload, dispatch the payload to the application (i.e. by yielding a result to an `IObservable`, or by collecting the result for dispatching in step 8)
  39. 7. Go to 4
  40. 8. Complete the invocation, dispatching the final payload item (if any) or the error (if any) to the application
  41. @@ -72,30 +72,30 @@ Invocations can be marked as "Non-Blocking" in the `Invocation` message, which i
  42. ## Streaming
  43. -The SignalR protocol allows for multiple `StreamItem` messages to be transmitted in response to an `Invocation` message, and allows the receiver to dispatch these results as they arrive, to allow for streaming data from one endpoint to another.
  44. +The SignalR protocol allows for multiple `StreamItem` messages to be transmitted in response to a `StreamingInvocation` message, and allows the receiver to dispatch these results as they arrive, to allow for streaming data from one endpoint to another.
  45. -On the Callee side, it is up to the Callee's Binder to determine if a method call will yield multiple results. For example, in .NET certain return types may indicate multiple results, while others may indicate a single result. Even then, applications may wish for multiple results to be buffered and returned in a single `Completion` frame. It is up to the Binder to decide how to map this. The Callee's Binder must encode each result in separate `StreamItem` messages, indicating the end of results by sending a `StreamCompletion` message.
  46. +On the Callee side, it is up to the Callee's Binder to determine if a method call will yield multiple results. For example, in .NET certain return types may indicate multiple results, while others may indicate a single result. Even then, applications may wish for multiple results to be buffered and returned in a single `Completion` frame. It is up to the Binder to decide how to map this. The Callee's Binder must encode each result in separate `StreamItem` messages, indicating the end of results by sending a `Completion` message.
  47. -On the Caller side, the user code which performs the invocation indicates how it would like to receive the results and it is up the Caller's Binder to handle the result. If the Caller expects only a single result, but multiple results are returned, or if the caller expects multiple results but only one result is returned, the Caller's Binder should yield an error. If the Caller wants to stop receiving `StreamItem` messages before the Callee sends a `StreamCompletion` message, the Caller can send a `CancelInvocation` message with the same `Invocation ID` used for the `Invocation` message that started the stream. When the Callee receives a `CancelInvocation` message it will stop sending `StreamItem` messages and will send a `StreamCompletion` message. The Caller is free to ignore any `StreamItem` messages as well as the `StreamCompletion` message after sending `CancelInvocation`.
  48. +On the Caller side, the user code which performs the invocation indicates how it would like to receive the results and it is up the Caller's Binder to handle the result. If the Caller expects only a single result, but multiple results are returned, or if the caller expects multiple results but only one result is returned, the Caller's Binder should yield an error. If the Caller wants to stop receiving `StreamItem` messages before the Callee sends a `Completion` message, the Caller can send a `CancelInvocation` message with the same `Invocation ID` used for the `StreamInvocation` message that started the stream. When the Callee receives a `CancelInvocation` message it will stop sending `StreamItem` messages and will send a `Completion` message. The Caller is free to ignore any `StreamItem` messages as well as the `Completion` message after sending `CancelInvocation`.
  49. ## Completion and results
  50. -An Invocation is only considered completed when the `Completion` or `StreamCompletion` message is received. Receiving **any** message using the same `Invocation ID` after a `Completion` or `StreamCompletion` message has been received for that invocation is considered a protocol error and the recipient may immediately terminate the connection.
  51. +An Invocation is only considered completed when the `Completion` message is received. Receiving **any** message using the same `Invocation ID` after a `Completion` message has been received for that invocation is considered a protocol error and the recipient may immediately terminate the connection.
  52. -If a Callee is going to stream results, it **MUST** send each individual result in a separate `StreamItem` message, and complete the invocation with a `StreamCompletion`. If the Callee is going to return a single result, it **MUST** not send any `StreamItem` messages, and **MUST** send the single result in a `Completion` message. This is to ensure that the Caller can unambiguously determine the intended streaming behavior of the method.
  53. +If a Callee is going to stream results, it **MUST** send each individual result in a separate `StreamItem` message, and complete the invocation with a `Completion`. If the Callee is going to return a single result, it **MUST** not send any `StreamItem` messages, and **MUST** send the single result in a `Completion` message. If the Callee receives an `Invocation` message for a method that would yield multiple results or the Callee receives a `StreamInvocationMessage` for a method that would return a single result it **MUST** complete the invocation with a `Completion` message containing an error.
  54. ## Errors
  55. -Errors are indicated by the presence of the `error` field in a `Completion` or `StreamCompletion` message. Errors always indicate the immediate end of the invocation. In the case of streamed responses, the arrival of a `StreamCompletion` message indicating an error should **not** stop the dispatching of previously-received results. The error is only yielded after the previously-received results have been dispatched.
  56. +Errors are indicated by the presence of the `error` field in a `Completion` message. Errors always indicate the immediate end of the invocation. In the case of streamed responses, the arrival of a `Completion` message indicating an error should **not** stop the dispatching of previously-received results. The error is only yielded after the previously-received results have been dispatched.
  57. If either endpoint commits a Protocol Error (see examples below), the other endpoint may immediately terminate the underlying connection.
  58. * It is a protocol error for any message to be missing a required field, or to have an unrecognized field.
  59. -* It is a protocol error for a Caller to send a `StreamItem`, `Completion` or `StreamCompletion` message with an `Invocation ID` that has not been received in an `Invocation` message from the Callee
  60. -* It is a protocol error for a Caller to send a `StreamItem`, `Completion` or `StreamCompletion` message in response to a Non-Blocking Invocation (see "Non-Blocking Invocations" above)
  61. -* It is a protocol error for a Caller to send a `Completion` message when a `StreamItem` message has previously been sent for the same `Invocation ID`.
  62. +* It is a protocol error for a Caller to send a `StreamItem` or `Completion` message with an `Invocation ID` that has not been received in an `Invocation` message from the Callee
  63. +* It is a protocol error for a Caller to send a `StreamItem` or `Completion` message in response to a Non-Blocking Invocation (see "Non-Blocking Invocations" above)
  64. +* It is a protocol error for a Caller to send a `Completion` message with a result when a `StreamItem` message has previously been sent for the same `Invocation ID`.
  65. * It is a protocol error for a Caller to send a `Completion` message carrying both a result and an error.
  66. -* It is a protocol error for an `Invocation` message to have an `Invocation ID` that has already been used by *that* endpoint. However, it is **not an error** for one endpoint to use an `Invocation ID` that was previously used by the other endpoint (allowing each endpoint to track it's own IDs).
  67. +* It is a protocol error for an `Invocation` or `StreamInvocation` message to have an `Invocation ID` that has already been used by *that* endpoint. However, it is **not an error** for one endpoint to use an `Invocation ID` that was previously used by the other endpoint (allowing each endpoint to track it's own IDs).
  68. ## Examples
  69. @@ -180,19 +180,19 @@ S->C: Completion { Id = 42, Result = [ 0, 1, 2, 3, 4 ] }
  70. ### Streamed Result (`Stream` example above)
  71. ```
  72. -C->S: Invocation { Id = 42, Target = "Stream", Arguments = [ 5 ] }
  73. +C->S: StreamInvocation { Id = 42, Target = "Stream", Arguments = [ 5 ] }
  74. S->C: StreamItem { Id = 42, Item = 0 }
  75. S->C: StreamItem { Id = 42, Item = 1 }
  76. S->C: StreamItem { Id = 42, Item = 2 }
  77. S->C: StreamItem { Id = 42, Item = 3 }
  78. S->C: StreamItem { Id = 42, Item = 4 }
  79. -S->C: StreamCompletion { Id = 42 }
  80. +S->C: Completion { Id = 42 }
  81. ```
  82. **NOTE:** The following is **NOT** an acceptable encoding of this invocation:
  83. ```
  84. -C->S: Invocation { Id = 42, Target = "Stream", Arguments = [ 5 ] }
  85. +C->S: StreamInvocation { Id = 42, Target = "Stream", Arguments = [ 5 ] }
  86. S->C: StreamItem { Id = 42, Item = 0 }
  87. S->C: StreamItem { Id = 42, Item = 1 }
  88. S->C: StreamItem { Id = 42, Item = 2 }
  89. @@ -200,18 +200,18 @@ S->C: StreamItem { Id = 42, Item = 3 }
  90. S->C: Completion { Id = 42, Result = 4 }
  91. ```
  92. -This is invalid because the `Completion` is not a valid message to complete a streaming invocation.
  93. +This is invalid because the `Completion` message for streaming invocations must not contain any result.
  94. ### Streamed Result with Error (`StreamFailure` example above)
  95. ```
  96. -C->S: Invocation { Id = 42, Target = "Stream", Arguments = [ 5 ] }
  97. +C->S: StreamInvocation { Id = 42, Target = "Stream", Arguments = [ 5 ] }
  98. S->C: StreamItem { Id = 42, Item = 0 }
  99. S->C: StreamItem { Id = 42, Item = 1 }
  100. S->C: StreamItem { Id = 42, Item = 2 }
  101. S->C: StreamItem { Id = 42, Item = 3 }
  102. S->C: StreamItem { Id = 42, Item = 4 }
  103. -S->C: StreamCompletion { Id = 42, Error = "Ran out of data!" }
  104. +S->C: Completion { Id = 42, Error = "Ran out of data!" }
  105. ```
  106. This should manifest to the Calling code as a sequence which emits `0`, `1`, `2`, `3`, `4`, but then fails with the error `Ran out of data!`.
  107. @@ -219,12 +219,12 @@ This should manifest to the Calling code as a sequence which emits `0`, `1`, `2`
  108. ### Streamed Result closed early (`Stream` example above)
  109. ```
  110. -C->S: Invocation { Id = 42, Target = "Stream", Arguments = [ 5 ] }
  111. +C->S: StreamInvocation { Id = 42, Target = "Stream", Arguments = [ 5 ] }
  112. S->C: StreamItem { Id = 42, Item = 0 }
  113. S->C: StreamItem { Id = 42, Item = 1 }
  114. C->S: CancelInvocation { Id = 42 }
  115. S->C: StreamItem { Id = 42, Item = 2} // This can be ignored
  116. -S->C: StreamCompletion { Id = 42 } // This can be ignored
  117. +S->C: Completion { Id = 42 } // This can be ignored
  118. ```
  119. ### Non-Blocking Call (`NonBlocking` example above)
  120. @@ -275,6 +275,29 @@ Example (Non-Blocking):
  121. }
  122. ```
  123. +### StreamInvocation Message Encoding
  124. +
  125. +A `StreamInvocation` message is a JSON object with the following properties:
  126. +
  127. +* `type` - A `Number` with the literal value 4, indicating that this message is a StreamInvocation.
  128. +* `invocationId` - A `String` encoding the `Invocation ID` for a message.
  129. +* `target` - A `String` encoding the `Target` name, as expected by the Callee's Binder.
  130. +* `arguments` - An `Array` containing arguments to apply to the method referred to in Target. This is a sequence of JSON `Token`s, encoded as indicated below in the "JSON Payload Encoding" section.
  131. +
  132. +Example:
  133. +
  134. +```json
  135. +{
  136. + "type": 4,
  137. + "invocationId": "123",
  138. + "target": "Send",
  139. + "arguments": [
  140. + 42,
  141. + "Test Message"
  142. + ]
  143. +}
  144. +```
  145. +
  146. ### StreamItem Message Encoding
  147. A `StreamItem` message is a JSON object with the following properties:
  148. @@ -344,33 +367,6 @@ Example - The following `Completion` message is a protocol error because it has
  149. }
  150. ```
  151. -### StreamCompletion Message Encoding
  152. -
  153. -A `StreamCompletion` message is a JSON object with the following properties
  154. -
  155. -* `type` - A `Number` with the literal value `4`, indicating that this message is a `StreamCompletion`.
  156. -* `invocationId` - A `String` encoding the `Invocation ID` for a message.
  157. -* `error` - A `String` encoding the error message.
  158. -
  159. -Example - A `StreamCompletion` message with no error
  160. -
  161. -```json
  162. -{
  163. - "type": 4,
  164. - "invocationId": "123"
  165. -}
  166. -```
  167. -
  168. -Example - A `StreamCompletion` message with an error
  169. -
  170. -```json
  171. -{
  172. - "type": 4,
  173. - "invocationId": "123",
  174. - "error": "It didn't work!"
  175. -}
  176. -```
  177. -
  178. ### CancelInvocation Message Encoding
  179. A `CancelInvocation` message is a JSON object with the following properties
  180. @@ -405,10 +401,10 @@ MessagePack uses different formats to encode values. Refer to the [MsgPack forma
  181. [1, InvocationId, NonBlocking, Target, [Arguments]]
  182. ```
  183. -* `1` - Message Type - `1` indicates this is an `Invocation` message
  184. -* InvocationId - A `String` encoding the Invocation ID for the message
  185. -* NonBlocking - A `Boolean` indicating if the invocation is Non-Blocking (see "Non-Blocking Invocations" above)
  186. -* Target - A `String` encoding the Target name, as expected by the Callee's Binder
  187. +* `1` - Message Type - `1` indicates this is an `Invocation` message.
  188. +* InvocationId - A `String` encoding the Invocation ID for the message.
  189. +* NonBlocking - A `Boolean` indicating if the invocation is Non-Blocking (see "Non-Blocking Invocations" above).
  190. +* Target - A `String` encoding the Target name, as expected by the Callee's Binder.
  191. * Arguments - An Array containing arguments to apply to the method referred to in Target.
  192. Example:
  193. @@ -438,6 +434,45 @@ is decoded as follows:
  194. * `0x91` - 1-element array (Arguments)
  195. * `0x2a` - `42` (Argument value)
  196. +### StreamInvocation Message Encoding
  197. +
  198. +`StreamInvocation` messages have the following structure:
  199. +
  200. +```
  201. +[4, InvocationId, Target, [Arguments]]
  202. +```
  203. +
  204. +* `4` - Message Type - `4` indicates this is a `StreamInvocation` message.
  205. +* InvocationId - A `String` encoding the Invocation ID for the message.
  206. +* Target - A `String` encoding the Target name, as expected by the Callee's Binder.
  207. +* Arguments - An Array containing arguments to apply to the method referred to in Target.
  208. +
  209. +Example:
  210. +
  211. +The following payload
  212. +
  213. +```
  214. +0x94 0x04 0xa3 0x78 0x79 0x7a 0xa6 0x6d 0x65 0x74 0x68 0x6f 0x64 0x91 0x2a
  215. +```
  216. +
  217. +is decoded as follows:
  218. +
  219. +* `0x94` - 4-element array
  220. +* `0x04` - `4` (Message Type - `StreamInvocation` message)
  221. +* `0xa3` - string of length 3 (InvocationId)
  222. +* `0x78` - `x`
  223. +* `0x79` - `y`
  224. +* `0x7a` - `z`
  225. +* `0xa6` - string of length 6 (Target)
  226. +* `0x6d` - `m`
  227. +* `0x65` - `e`
  228. +* `0x74` - `t`
  229. +* `0x68` - `h`
  230. +* `0x6f` - `o`
  231. +* `0x64` - `d`
  232. +* `0x91` - 1-element array (Arguments)
  233. +* `0x2a` - `42` (Argument value)
  234. +
  235. ### StreamItem Message Encoding
  236. `StreamItem` messages have the following structure:
  237. @@ -543,58 +578,6 @@ is decoded as follows:
  238. * `0x03` - `3` (ResultKind - Non-Void result)
  239. * `0x2a` - `42` (Result)
  240. -### StreamCompletion Message Encoding
  241. -
  242. -`StreamCompletion` messages have the following structure
  243. -
  244. -```
  245. -[4, InvocationId, Error?]
  246. -```
  247. -
  248. -* `4` - Message Type - `4` indicates this is a `StreamCompletion` message
  249. -* InvocationId - A `String` encoding the Invocation ID for the message
  250. -* Error - An optional string containing an error message if the invocation failed. Absent if the invocation completed without error.
  251. -
  252. -Examples:
  253. -
  254. -#### Completion for Successful Invocations
  255. -
  256. -The following payload:
  257. -```
  258. -0x92 0x04 0xa3 0x78 0x79 0x7a
  259. -```
  260. -
  261. -is decoded as follows:
  262. -
  263. -* `0x92` - 2-element array
  264. -* `0x04` - `4` (Message Type - `StreamCompletion` message)
  265. -* `0xa3` - string of length 3 (InvocationId)
  266. -* `0x78` - `x`
  267. -* `0x79` - `y`
  268. -* `0x7a` - `z`
  269. -
  270. -#### Completion for Failed Invocations
  271. -
  272. -The following payload:
  273. -```
  274. -0x93 0x04 0xa3 0x78 0x79 0x7a 0xa5 0x45 0x72 0x72 0x6f 0x72
  275. -```
  276. -
  277. -is decoded as follows:
  278. -
  279. -* `0x93` - 3-element array
  280. -* `0x04` - `4` (Message Type - `StreamCompletion` message)
  281. -* `0xa3` - string of length 3 (InvocationId)
  282. -* `0x78` - `x`
  283. -* `0x79` - `y`
  284. -* `0x7a` - `z`
  285. -* `0xa5` - string of length 5
  286. -* `0x45` - `E`
  287. -* `0x72` - `r`
  288. -* `0x72` - `r`
  289. -* `0x6f` - `o`
  290. -* `0x72` - `r`
  291. -
  292. ### CancelInvocation Message Encoding
  293. `CancelInvocation` messages have the following structure