Commit 5faa3c2f authored by totten's avatar totten
Browse files

Add "Pipe Reference"

parent 5c8e7d05
# Pipe Reference
The `Civi::pipe` transport provides a request-response mechanism for executing multiple tasks within a single CiviCRM process. It uses
a line-oriented message protocol and complies with [JSON-RPC v2.0](https://www.jsonrpc.org/specification). The transport
is compatible with several mechanisms for bootstrapping CiviCRM.
The list of valid method calls is purposefully short. It allows for establishing/testing a session, authenticating, and sending
API calls (APIv3 and APIv4).
## Example
This example connects to a CiviCRM site running in `/var/www/example.com/web` and issues two requests.
In `bash`, open the pipe and receive a welcome message:
```bash
$ cd /var/www/example.com/web
$ cv ev 'Civi::pipe();'
{"Civi::pipe":{"v":"5.46.alpha1","t":"trusted","l":["login"]}}
```
Within this pipe, exchange requests:
```javascript
// Send request for `echo("hello world")`
{"jsonrpc":"2.0","method":"echo","params":["hello world"],"id":null}
// Receive response "hello world"
{"jsonrpc":"2.0","result":["hello world"],"id":null}
// Send request for `api3("System", "flush")`
{"jsonrpc":"2.0","method":"api3","params":["System","flush",{"check_permissions":0}],"id":null}
// Receive response
{"jsonrpc":"2.0","result":{"is_error":0,"version":3,"count":1,"values":1},"id":null}
```
## Overview
The general flow for a pipe session is:
1. A client opens a connection to `Civi::pipe()`
2. The server responds with a header (UTF-8, one-line, JSON).
3. The client submits a request (UTF-8, one-line, JSON-RPC).
4. The server sends a response (UTF-8, one-line, JSON-RPC).
5. Go back to (3).
Requests are executed synchronously with a single thread of operation (*one request then one response*). This parallels the
PHP-HTTP architecture ordinarily used by CiviCRM (*one process handles one request at a time*). However, it expands the lifetime
of the PHP process to serve multiple requests. This avoids redundant bootstraps. It is suitable for a series of requests with a
common context (*eg several requests by the same user*).
The pipe protocol is specifically focused on two-channel communication (`STDIN`/`STDOUT`). If there is a third (`STDERR`)
channel, then the client MAY log or display it for debugging purposes. However, `STDERR` must be ignored when parsing requests
and responses.
## Connection
### CLI
The function `Civi::pipe()` begins the server process for evaluating requests. Here are a few examples to invoke `Civi::pipe()`:
```bash
## Start with cv
$ cd /var/www/example.com/web
$ cv ev 'Civi::pipe();'
{"Civi::pipe":{"v":"5.46.alpha1","t":"trusted","l":["login"]}}
## Start with drush
$ cd /var/www/example.com/web
$ drush ev 'civicrm_initialize(); Civi::pipe();'
{"Civi::pipe":{"v":"5.46.alpha1","t":"trusted","l":["login"]}}
## Start with wp-cli
$ cd /var/www/example.com/web
$ wp eval 'civicrm_initialize(); Civi::pipe();'
{"Civi::pipe":{"v":"5.46.alpha1","t":"trusted","l":["login"]}}
## Start with SSH + cv
$ ssh webuser@backend.example.com cv ev --cwd=/var/www/example.com/web ev "Civi::pipe();"
{"Civi::pipe":{"v":"5.46.alpha1","t":"trusted","l":["login"]}}
```
At this point, the connection is established, and you may send requests.
### Flags
When connecting, the pipe provides a welcome message with a series of flags.
Advanced clients may request a list of connection flags when calling `Civi::pipe()`. Connection flags are useful for both *reporting* about the
connection and *requesting* specific features.
For example, this requests two flags (`v`, `u`) and receives a welcome message:
```php
Civi::pipe("vu");
```
```js
{"Civi::pipe":{"v":"5.46.alpha1","u":"untrusted"}
```
The `v` flag reports on the active CiviCRM version. The `u` flag marks the session as *untrusted* (which ensures that API calls must enforce permissions).
Valid flags are:
* `v` (*version*): Report the CiviCRM version. Ex: `"v":"5.48.0"`
* `j` (*json-rpc*): Report supported flavors of JSON-RPC. Ex: `"j":["jsonrpc-2.0"]`
* `l` (*login*): Report login options. Ex: `"l":["login"]` and `"l":["nologin"]`
* `t` (*trusted*): Mark session as trusted. Logins do not require credentials. API calls may execute with or without permission-checks.
* `u` (*untrusted*): Mark session as untrusted. Logins require credentials. API calls may only execute with permission-checks.
Unrecognized flags must not cause a failure. They must report as `null`. In this example, `x` is an unrecognized flag:
```php
Civi::pipe("uxv");
```
```javascript
{"Civi::pipe":{"u":"untrusted","x":null,"v":"5.48.0"}
```
## Line format
Each request and response is a JSON object formatted to fit on a single line. Lines are separated with the
conventional newline character (`\n`).
Payload data MAY include escaped newlines (`"\n"`).
JSON is rendered in condensed / ugly / non-"pretty-printed" format. It MUST NOT include the line-delimiter as whitespace.
!!! warning "For readability, some long examples in the documentation may be displayed with multiple lines. But these lines are not transmitted over the wire."
Each request-line and each response-line is formatted according to [JSON-RPC v2.0](https://www.jsonrpc.org/specification).
Many PHP deployments include misconfigurations, bugs, or add-ons -- which can cause extra noise to be presented on STDOUT.
Clients SHOULD use the [session option `responsePrefix`](#options) to detect and discard noise.
## Methods
### `api3`
The `api3` method is used to invoke APIv3 actions. It receives the standard tuple of entity-action-params.
For example, to send a request for `Contact.get` with `rowCount=4` and `check_permissions=false`:
```javascript
> {"jsonrpc":"2.0",
"method":"api3",
"params":["Contact","get",{"rowCount":4,"check_permissions":false}],
"id":null}
< {"jsonrpc":"2.0",
"result":{"is_error":0,"count":4,values:[...]},
"id":null}
```
By defaut, APIv3 errors are converted to JSON-RPC error-format. For some use-cases, this could lose precision or
interoperability. You may change the error format with [the `apiError` option](#options).
By default, APIv3 requests received on `Civi::pipe()` will enforce permission-checks. On trusted connections,
you may opt-out with `"check_permissions":false` or [the `apiCheckPermissions` option](#options).
### `api4`
The `api4` method is used to invoke APIv4 actions. It receives the standard tuple of entity-action-params.
```javascript
> {"jsonrpc":"2.0",
"method":"api4",
"params":["Contact","get",{"limit":4,"checkPermissions":false}],
"id":null}
< {"jsonrpc":"2.0",
"result":[{"id":1,"contact_type":"Organization",...}],
"id":null}
```
By defaut, APIv4 errors are converted to JSON-RPC error-format. For some use-cases, this could lose precision or
interoperability. You may change the error format with [the `apiError` option](#options).
By default, APIv4 requests received on `Civi::pipe()` will enforce permission-checks. On trusted connections,
you may opt-out with `"checkPermissions":false` or [the `apiCheckPermissions` option](#options).
### `echo`
The `echo` message is used for testing. It simply returns the input.
```javascript
> {"jsonrpc":"2.0","method":"echo","params":["hello world"],"id":null}
< {"jsonrpc":"2.0","result":["hello world"],"id":null}
```
### `login`
Set the active user/contact.
```javascript
> {"jsonrpc":"2.0","method":"login","params":{"contactId":202},"id":null}
< {"jsonrpc":"2.0","result":{"contactId":202,"userId":1},"id":null}
```
Note: Only trusted parties are allowed to connect over the pipe medium. The `login` method
sets the active user but does not authenticate credentials.
The login principal may be specified with any one of the following:
* `contactId` (`int`)
* `userId` (`int`)
* `user` (`string`)
### `options`
The `options` method manages connectivity options. By default, it will return a list of all known options:
```javascript
> {"jsonrpc":"2.0",
"method":"options",
"id":null}
< {"jsonrpc":"2.0",
"result":{"responsePrefix":null,"bufferSize":524288},
"id":null}
```
Additionally, you may use it to *update* an option. Any modified options will be returned.
```javascript
> {"jsonrpc":"2.0",
"method":"options",
"params":{"responsePrefix":"\u0001\u0001"},
"id":null}
< {"jsonrpc":"2.0",
"result":{"responsePrefix":"\u0001\u0001"},
"id":null}
```
The following options are defined:
* `apiCheckPermissions` (`bool`): By default, on all connections, API calls are executed with the
`checkPermissions`/`check_permission` flag. This default may be switched off (*for trusted connections*).
* `apiError` (`string`): Specify how CiviCRM APIs should report their errors. Either:
* `array`: Errors are reported as part of the API's result-array. Useful for precise+generic handling.
* `exception`: Errors are converted to exceptions and then to JSON-RPC errors. Some JSON-RPC clients
will convert these to client-side exceptions.
* `bufferSize` (`int`): The maximum length of a line in the control session, measured in bytes.
This determines the maximum request size. (The default value is deployment-specific/implementation-specific.
The default must be at least 64kb. At time of writing, the default for civicrm-core is 512kb.)
* `responsePrefix` (`string`): Before sending any response (but after evaluating the request), send
an extra prefix or delimiter. (Defensively-coded clients may set a prefix and watch for it. If any
output comes before the prefix, then the client may infer that the server is misbehaved - eg a debug
hack or a bad plugin is creating interference. Disregard output before the prefix.)
## Special cases
* If a request-line is received with an empty-string, it is ignored by the server.
......@@ -327,6 +327,7 @@ nav:
- Transaction Reference: framework/database/transactions.md
- File System: framework/filesystem.md
- OAuth Reference: framework/oauth.md
- Pipe Reference: framework/pipe.md
- PseudoConstant Reference: framework/pseudoconstant.md
- QuickForm Reference:
- QuickForm: framework/quickform/index.md
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment