| # Dart Tooling Daemon Protocol |
| |
| This document describes the Dart Tooling Daemon protocol. This daemon, and it's |
| protocol is meant to facilitate communication channels between various tools. |
| |
| To start a tooling daemon run `dart tooling-daemon`. |
| |
| If you have access to `Dart` then you can use the [package:dtd](#packagedtd) |
| package. |
| |
| Otherwise, if you are creating your own client that will communicate with DTD |
| using RPC, make sure to follow the entire |
| [Dart Tooling Daemon RPC protocol](#dart-tooling-daemon-rpc-protocol) section. |
| |
| The Dart Tooling Daemon Protocol uses JSON-RPC 2.0. |
| |
| ## Common Services Definitions |
| |
| Some common services are defined to ensure that different tools can provide the |
| same functionality consistently to DTD clients. Details of these can be found in |
| [dtd_common_services](./dtd_common_services.md). |
| |
| ## Visualizing DTD interactions |
| |
| The following diagrams visualize how the Dart Tooling Daemon interacts with |
| clients. |
| |
| ### Streams |
| |
| The following is a visualization of normal stream interaction. It is presented |
| in function notation, instead of RPC calls, for brevity. |
| |
| ```mermaid |
| sequenceDiagram |
| participant c1 as Client 1 |
| participant c2 as Client 2 |
| participant dtd as Dart Tooling Daemon |
| participant c3 as Client 3 |
| |
| c1->>dtd: streamListen(streamId: "foo") |
| activate dtd |
| dtd-->>c1: Success |
| deactivate dtd |
| |
| c2->>dtd: streamListen(streamId: "foo") |
| activate dtd |
| dtd-->>c2: Success |
| deactivate dtd |
| |
| Note right of c2: Client 1 and Client 2 are now listening to the "foo" stream. |
| Note right of dtd: Client 3 posts an event to the "foo" stream. |
| |
| c3->>dtd: postEvent( streamId: "foo", eventKind: "example", eventData: { "bar": "baz" }) |
| activate dtd |
| dtd-->>c3: Success |
| |
| Note right of c2: Client 1 and Client 2 are forwarded the event using streamNotify. |
| dtd-->>c2: streamNotify( streamId: "foo", eventKind: "example", eventData: { "bar": "baz" }) |
| dtd-->>c1: streamNotify( streamId: "foo", eventKind: "example", eventData: { "bar": "baz" }) |
| deactivate dtd |
| |
| Note right of c2: Client 2 now cancels listening to the "foo" stream. |
| c2-->>dtd: streamCancel(streamId: "foo") |
| activate dtd |
| dtd-->>c2: Success |
| deactivate dtd |
| |
| Note right of dtd: Client 3 posts another event to the "foo" stream. |
| c3->>dtd: postEvent( streamId: "foo", eventKind: "example", eventData: { "bar": "baz 2" }) |
| activate dtd |
| dtd-->>c3: Success |
| |
| Note right of c2: Client 1 is still listening to the "foo" stream, so the event<br/>is still forwarded to it using streamNotify. |
| dtd-->>c1: streamNotify( streamId: "foo", eventKind: "example", eventData: { "bar": "baz2 " }) |
| deactivate dtd |
| ``` |
| |
| ### Service Methods |
| |
| The following is a visualization of normal service method interaction. It is |
| presented in function notation, instead of RPC calls, for brevity. |
| |
| ```mermaid |
| sequenceDiagram |
| participant c1 as Client 1 |
| participant dtd as Dart Tooling Daemon |
| participant c2 as Client 2 |
| |
| Note right of c1: Client 1 registers the Foo.bar method. |
| c1->>dtd: registerService(service: "Foo", method: "bar") |
| activate dtd |
| dtd-->>c2: ServiceRegistered({"service": "Foo", "method": "bar"}) |
| dtd-->>c1: Success |
| deactivate dtd |
| |
| Note left of c2: Client 2 calls Foo.bar |
| c2->>dtd: Foo.bar({{"a": 1, "b": 2}}) |
| activate dtd |
| |
| Note left of dtd: dtd forwards the service method call to Client 1. |
| dtd->>c1: Foo.bar({{"a": 1, "b": 2}}) |
| activate c1 |
| |
| Note right of c1: Client 1 handles the<br/>request and responds to the Dart Tooling Daemon. |
| c1-->>dtd: {"example": "response"} |
| deactivate c1 |
| |
| Note right of dtd: Dart Tooling Daemon forwards the response to Client 2. |
| dtd-->>c2: {"example": "response"} |
| deactivate dtd |
| |
| Note right of c1: Client 1 disconnects. |
| activate dtd |
| dtd-->>c2: ServiceUnregistered({"service": "Foo", "method": "bar"}) |
| deactivate dtd |
| ``` |
| |
| `registerService` takes an optional map of capabilities that can be used for |
| evolving the service method over time (such as expressing support for additional |
| named values in the arguments). |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "registerService", |
| "params": { |
| "service": "Foo", |
| "method": "bar", |
| "capabilities": { |
| "supportsAdditionalFoo": true, |
| }, |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| Notifications will be sent over a special `Service` stream when services are |
| registered and unregistered. |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "streamNotify", |
| "params": { |
| "streamId": "Service", |
| "eventKind": "ServiceRegistered", |
| "eventData": { |
| "service": "Foo", |
| "method": "bar", |
| // Capabilities are included only if the client provided them |
| "capabilities": { |
| "supportsAdditionalFoo": true, |
| }, |
| } |
| }, |
| "id": "2" |
| } |
| |
| { |
| "jsonrpc": "2.0", |
| "method": "streamNotify", |
| "params": { |
| "streamId": "Service", |
| "eventKind": "ServiceUnregistered", |
| "eventData": { |
| "service": "Foo", |
| "method": "bar", |
| // Unregistered events do not contain capabilities |
| } |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| ## Dart Tooling Daemon Interaction |
| |
| ### package:dtd |
| |
| [package:dtd](https://pub.dev/packages/dtd) is available for use with Dart. |
| It facilitates access to the Dart Tooling Daemon, by providing a convenient Dart |
| interface. |
| |
| ### Dart Tooling Daemon RPC protocol |
| |
| To facilitate RPC interaction with the Dart Tooling Daemon, this section lays |
| out the methods and interactions involved. |
| |
| If you are implementing a custom client then make sure to follow all of the |
| parts in this section, including the |
| [Custom client methods](#custom-client-methods) section. |
| |
| #### `streamListen` |
| |
| Registers the calling client as listening to the stream named _streamId_. |
| |
| The calling client will then receive [streamNotify](#streamnotify) calls when |
| any client calls [postEvent](#postevent) on the _streamId_ stream. |
| |
| ##### Parameters |
| |
| `String streamId` - The name of the stream to start listening to. |
| |
| ##### Result |
| |
| If successful, responds with [Success](#success-responses). |
| |
| If the client is already subscribed to the stream, the _103_ (Stream already |
| subscribed) [RPC error](#rpc-errors) code is returned. |
| |
| ##### Code Sample |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "streamListen", |
| "params": { |
| "streamId": "foo_stream" |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| ##### Response |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "result": {"type": "Success"}, |
| "id": "2" |
| } |
| ``` |
| |
| #### `streamCancel` |
| |
| Unregisters the calling client from listening to the stream named _streamId_. |
| |
| The calling client will no longer receive [streamNotify](#streamnotify) method |
| calls for events on the _streamId_ stream. |
| |
| ##### Parameters |
| |
| `String streamId` - The name of the stream to stop listening to. |
| |
| ##### Result |
| |
| If successful, responds with [Success](#success-responses). |
| |
| If the client is not subscribed to the stream, the _104_ (Stream not |
| subscribed) [RPC error](#rpc-errors) code is returned. |
| |
| ##### Code Sample |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "streamCancel", |
| "params": { |
| "streamId": "foo_stream" |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| ##### Response |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "result": {"type": "Success"}, |
| "id": "2" |
| } |
| ``` |
| |
| #### `postEvent` |
| |
| Calls [streamNotify](#streamnotify) on all clients that are currently |
| [streamListen](#streamlisten)ing to the _streamId_ stream. |
| |
| ##### Parameters |
| |
| - `String streamId` - The stream to post the event to |
| - `String eventKind` - The kind of event being sent. |
| - `Map<String, Object?> eventData` - A map object of data to send with the |
| event. _eventData_ must be serializable to json. |
| |
| ##### Result |
| |
| If successful, responds with [Success](#success-responses). |
| |
| ##### Code Sample |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "postEvent", |
| "params": { |
| "streamId": "fooStream", |
| "eventKind": "bar", |
| "eventData": { "bazKey": "apple" } |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| ##### Response |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "result": {"type": "Success"}, |
| "id": "2" |
| } |
| ``` |
| |
| #### `registerService` |
| |
| Registers the calling client as the handler for any service method calls where |
| the service is _service_ and the method is _method_. |
| |
| Service names may not include dots `.` (though method names may), but service |
| names cannot be reused across clients. If one client has registered any method |
| for the service "Foo", then no other client can register a method under the |
| service "Foo". |
| |
| To call the method registered by this call, a client can send a |
| [service method](#servicemethod) call to the Dart Tooling Daemon. |
| |
| When a client disconnects, then all of the service methods registered to it are |
| removed. |
| |
| ##### Parameters |
| |
| - `String service` - The name of the service to register the client to. Once a |
| client has registered a method to a service then only that client can register |
| other methods to that service. |
| - `String method` - The name of the method to register to _service_. |
| |
| ##### Result |
| |
| If successful, responds with [Success](#success-responses). |
| |
| If the _service_ has already been registered by another client, the _111_ |
| (Service already registered) [RPC error](#rpc-errors) code is returned. Only one |
| client at a time may register to a [service]. Once a client disconnects then |
| another client may register services under that name. |
| |
| If the _method_ has already been registered on the _service_, the _132_ |
| (Service method already registered) [RPC error](#rpc-errors) code is returned. |
| |
| ##### Code Sample |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "registerService", |
| "params": { |
| "service": "Foo", |
| "method": "bar", |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| ##### Response |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "result": {"type": "Success"}, |
| "id": "2" |
| } |
| ``` |
| |
| #### `service`.`method` |
| |
| Triggers the service method registered by [registerService](#registerservice), |
| for _service_ and _method_. |
| |
| Dart Tooling Daemon will forward the service method call to the client that |
| [registered](#registerservice) it. The client's response will be returned from |
| this method call. |
| |
| ##### Parameters |
| |
| The parameters must be a `Map<String, Object?>` where the contents are defined |
| on a case by case basis on the expectations of the client who registered |
| the service method. |
| |
| ##### Result |
| |
| The result is defined on a case by case basis based on the implementer of the |
| service method. |
| |
| If service method does not exist, the -32601 (Method not found) |
| [RPC error](#rpc-errors) code is returned. |
| |
| ##### Code Sample |
| |
| Assume that a client has registered a service method with: |
| |
| - service: `Foo` |
| - method: `bar` |
| |
| Then calling that service method might look like: |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "Foo.bar", |
| "params": { "baz": 3 }, |
| "id": "2" |
| } |
| ``` |
| |
| ##### Response |
| |
| The response is defined on a case by case basis based on the implementer of the |
| service method. |
| |
| #### Custom Client Methods |
| |
| When creating a client that interacts with the Dart Tooling Daemon, register |
| the following methods on your client in order to participate in the full |
| lifecycle. |
| |
| ##### `streamNotify` |
| |
| In order to handle messages posted to streams that the client is |
| [streamListen](#streamlisten)ing to, then the calling client may handle incoming |
| `streamNotify` RPC methods. |
| |
| When any client calls [postEvent](#postevent) for a given _streamId , if a |
| client has[streamListen](#streamlisten)ed to _streamId_, then that client |
| will receive `streamNotify` method calls for those events. Implement this method |
| in order to receive those calls. |
| |
| ###### Parameters |
| |
| - `String streamId` - The stream to post the event to |
| - `String eventKind` - The kind of event being sent. |
| - `Map<String, Object?> eventData` - A map object of data to send with the |
| event. |
| |
| ###### Result |
| |
| No response is given since this is sent as a notification. |
| |
| ###### Code Sample |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "streamNotify", |
| "params": { |
| "streamId": "fooStream", |
| "eventKind": "bar", |
| "eventData": { "baz": "car" } |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| ###### Response |
| |
| None, `streamNotify` is called as a notification and expects no response. |
| |
| ##### `service.method` |
| |
| Any time the client calls [registerService](#registerservice) then it will |
| receive method calls for that service method. |
| |
| The method name will take the form `service`.`method`. Any response returned by |
| the RPC handler will be forwarded to the service method caller. |
| |
| ###### Parameters |
| |
| The parameters are defined on a case by case basis based on the expectations of |
| the client who registered the service method. |
| |
| ###### Result |
| |
| The result is defined on a case by case basis based on the implementer of the |
| service method. |
| |
| ###### Code Sample |
| |
| Assume that a client has registered a service method with: |
| |
| - service: `Foo` |
| - method: `bar` |
| |
| Then calling that service method might look like: |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "Foo.bar", |
| "params": { "baz": 3 }, |
| "id": "2" |
| } |
| ``` |
| |
| ###### Response |
| |
| The response is defined on a case by case basis based on the implementer of the |
| service method. It should be a valid RPC response. |
| |
| |
| ## FileSystem service |
| |
| The `FileSystem` service is coupled tightly with the Dart Tooling Daemon, and |
| will be present immediately when the Dart Tooling Daemon has started. This |
| describes the RPC methods used to interact with the `FileSystem` service. |
| |
| ### readFileAsString |
| |
| Reads the contents of the file at _uri_ on the system where the Dart Tooling |
| Daemon is running. |
| |
| #### Parameters |
| |
| - `String uri` - An absolute path to the file to read. This should be using the |
| 'file' scheme and be an absolute path. |
| |
| #### Result |
| |
| If successful, responds with [_FileContent_](#filecontent). |
| |
| If the file at _uri_ does not exist, the 141 (The file does not exist) |
| [RPC error](#rpc-errors) code is returned. |
| |
| If the _uri_ is not contained in the |
| [IDE Workspace Roots](#ide-workspace-roots), the 142 |
| (Permission denied) [RPC error](#rpc-errors) code is returned. |
| |
| If _uri_ does not have a 'file' scheme, the 143 (File scheme expected on uri) |
| [RPC error](#rpc-errors) code is returned. |
| |
| #### Code Sample |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "FileSystem.readFileAsString", |
| "params": { |
| "uri": "file:///path/to/file.txt", |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| #### Response |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "result": { |
| "type": "FileContent", |
| "content": "The contents\nof the file", |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| ### writeFileAsString |
| |
| Writes the contents of the file at _uri_ on the system where the Dart |
| Tooling Daemon is running. If the file, or parent directories don't exist then |
| they are created. |
| |
| #### Parameters |
| |
| - `String uri` - An absolute path to the file to read. This should be using the |
| 'file' scheme and be an absolute path. |
| - `String contents` - The content to write to _uri_. |
| |
| #### Result |
| |
| If successful, responds with [Success](#success-responses). |
| |
| If the _uri_ is not contained in the |
| [IDE Workspace Roots](#ide-workspace-roots), the 142 |
| (Permission denied) [RPC error](#rpc-errors) code is returned. |
| |
| If _uri_ does not have a 'file' scheme, the 143 (File scheme expected on uri) |
| [RPC error](#rpc-errors) code is returned. |
| |
| #### Code Sample |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "FileSystem.writeFileAsString", |
| "params": { |
| "uri": "file:///path/to/file.txt", |
| "contents": "Some contents to write", |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| #### Response |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "result": {"type": "Success"}, |
| "id": "2" |
| } |
| ``` |
| |
| ### listDirectoryContents |
| |
| Reads the entries of the directory at _uri_ on the system where the Dart Tooling |
| Daemon is running. |
| |
| #### Parameters |
| |
| - `String uri` - An absolute path to the file to read. This should be using the |
| 'file' scheme and be an absolute path. |
| |
| #### Result |
| |
| If successful, responds with [_UriList_](#urilist). |
| |
| If the _uri_ is not contained in the |
| [IDE Workspace Roots](#ide-workspace-roots), the 142 |
| (Permission denied) [RPC error](#rpc-errors) code is returned. |
| |
| If _uri_ does not have a 'file' scheme, the 143 (File scheme expected on uri) |
| [RPC error](#rpc-errors) code is returned. |
| |
| #### Code Sample |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "FileSystem.listDirectoryContents", |
| "params": { |
| "uri": "file:///path/to/dir/", |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| #### Response |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "result": { |
| "type": "UriList", |
| "uris": [ |
| "file:///path/to/dir/a.txt", |
| "file:///path/to/dir/b/", |
| ], |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| ### IDE Workspace Roots |
| |
| The IDE workspace roots are used to ensure the `FileSystem` service methods |
| do not perform file operations in areas outside of the IDE workspace roots. |
| |
| When the Dart Tooling Daemon is started, a secret is generated and presented to |
| the process that started the Dart Tooling Daemon. That secret is then used to |
| authenticate when calling [setIDEWorkspaceRoots](#setideworkspaceroots). This |
| ensures that the process that started the Dart Tooling Daemon is the one setting |
| the IDE workspace root. |
| |
| Once the IDE workspace roots are set then `FileSystem` service methods will be |
| limited to those workspace directories. Any operations outside of them will |
| result with the 142 (Permission denied) [RPC error](#rpc-errors) code. |
| |
| If no workspace roots are set then all `FileSystem` service method calls will |
| fail with the 142 (Permission denied) [RPC error](#rpc-errors) code. |
| |
| #### setIDEWorkspaceRoots |
| |
| ##### Parameters |
| |
| - `String secret` - The secret that was passed back to the calling process when |
| the Dart Tooling Daemon was started. |
| - `List<String> roots` - The directories to set as the IDE workspace roots. |
| These must be uris with a 'file' scheme. |
| |
| ##### Result |
| |
| If successful, responds with [Success](#success-responses). |
| |
| If _secret_ does not match the secret created when Dart Tooling Daemon was |
| created then 142 (Permission denied) [RPC error](#rpc-errors) code is returned. |
| |
| If one of the _roots_ is missing a 'file' scheme, the 143 (File scheme expected on uri) |
| [RPC error](#rpc-errors) code is returned. |
| |
| ##### Code Sample |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "FileSystem.setIDEWorkspaceRoots", |
| "params": { |
| "secret": "aBVsK8nRfdJsDEnS", |
| "roots": [ |
| "file:///path/to/dir/a/", |
| "file:///path/to/dir/b/", |
| ], |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| ##### Response |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "result": {"type": "Success"}, |
| "id": "2" |
| } |
| ``` |
| |
| #### getIDEWorkspaceRoots |
| |
| Used to get the IDE workspace roots that have been set using |
| [setIDEWorkspaceRoots](#setideworkspaceroots). |
| |
| ##### Parameters |
| |
| Takes no parameters. |
| |
| ##### Result |
| |
| If successful, responds with [_IDEWorkspaceRoots_](#ideworkspaceroots). |
| |
| ##### Code Sample |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "FileSystem.getIDEWorkspaceRoots", |
| "params": {}, |
| "id": "2" |
| } |
| ``` |
| |
| ##### Response |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "result": { |
| "type": "IDEWorkspaceRoots", |
| "ideWorkspaceRoots":: [ |
| "file:///path/to/dir/a/", |
| "file:///path/to/dir/b/", |
| ], |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| #### getProjectRoots |
| |
| Used to get the project roots contained within the current set of IDE workspace roots |
| (see [setIDEWorkspaceRoots](#setideworkspaceroots) and |
| [getIDEWorkspaceRoots](#getideworkspaceroots)). A project root is any directory that |
| contains a `pubspec.yaml` file. |
| |
| ##### Parameters |
| |
| - `int depth` - The depth that each IDE workspace root directory tree will be searched |
| for project roots. Defaults to 4. |
| |
| ##### Result |
| |
| If successful, responds with [_UriList_](#urilist). |
| |
| ##### Code Sample |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "method": "FileSystem.getProjectRoots", |
| "params": {"depth": 5}, |
| "id": "2" |
| } |
| ``` |
| |
| ##### Response |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "result": { |
| "type": "UriList", |
| "uris": [ |
| "file:///path/to/dir/a/", |
| "file:///path/to/dir/b/", |
| ], |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| ### File System Service Types |
| |
| The File System Service Types are RPC data responses used to facilitate |
| communication with the `FileSystem` service methods. They are used by |
| [pkg/dtd_impl](./) and [`package:dtd`](../pkg/) to build and parse responses for |
| the different service methods. |
| |
| #### `UriList` |
| |
| Response for communicating a list of URIs. |
| |
| The returned uris will be `file://` Uris. |
| |
| Used by `FileSystem.listDirectoryContents`, and `FileSystem.getProjectRoots`. |
| |
| ```json |
| { |
| "type": "UriList", |
| "uris": [ |
| "file:///path/to/file.txt", |
| "file:///path/to/dir/", |
| ], |
| } |
| ``` |
| |
| #### `FileContent` |
| |
| Response for communicating the contents of a file. |
| |
| Used by `FileSystem.readFileAsString` |
| |
| ```json |
| { |
| "type": "FileContent", |
| "content": "The contents of a file.", |
| } |
| ``` |
| |
| #### `IDEWorkspaceRoots` |
| |
| Response for communicating workspace roots. |
| |
| The returned uris will be `file://` Uris. |
| |
| Used by `FileSystem.getIDEWorkspaceRoots` |
| |
| ```json |
| { |
| "type": "IDEWorkspaceRoots", |
| "ideWorkSpaceRoots": [ |
| "file:///path/to/root/A/", |
| "file:///path/to/root/B/", |
| ], |
| } |
| ``` |
| |
| ## DTD RPC Responses |
| |
| ### RPC Errors |
| |
| When a request to Dart Tooling Daemon encounters an error, it is provided in the _error_ |
| property of the response object. JSON-RPC errors always provide |
| _code_, _message_, and _data_ properties. |
| |
| Here is an example error response for our [streamListen](#streamlisten) |
| request above. This error would be generated if we were attempting to |
| subscribe to the _GC_ stream multiple times from the same client. |
| |
| ```json |
| { |
| "jsonrpc": "2.0", |
| "error": { |
| "code": 103, |
| "message": "Stream already subscribed", |
| "data": { |
| "details": "The stream 'GC' is already subscribed" |
| } |
| }, |
| "id": "2" |
| } |
| ``` |
| |
| In addition to the |
| [error codes](http://www.jsonrpc.org/specification#error_object) specified in |
| the JSON-RPC spec, we use the following application specific error codes: |
| code | message | meaning |
| ---- | ------- | ------- |
| -32601 | Method not found | The method does not exist / is not available. |
| -32602 | Invalid params | Invalid params. Invalid method parameter(s). |
| 103 | Stream already subscribed | The client is already subscribed to the specified _streamId_. |
| 104 | Stream not subscribed | The client is not subscribed to the specified _streamId_. |
| 111 | Service already registered | Service with such name has already been registered by this client. |
| 112 | Service disappeared | Failed to fulfill service request, likely service handler is no longer available. |
| 132 | Service method already registered | Method for the given service has already been registered by this client. |
| 140 | The directory does not exist | The specified directory does not exist. |
| 141 | The file does not exist | The specified file does not exist. |
| 142 | Permission denied | Permission has been denied for the requested action. |
| 143 | File scheme expected on uri | The uri parameter should be passed as a uri with a file scheme. |
| |
| ### Success Responses |
| |
| Methods that respond with `Success` do so with the following RPC. |
| |
| ```json |
| {"id": "2", "type": "Success"} |
| ``` |