.. Licensed to the Apache Software Foundation (ASF) under one
   or more contributor license agreements.  See the NOTICE file
   distributed with this work for additional information
   regarding copyright ownership.  The ASF licenses this file
   to you under the Apache License, Version 2.0 (the
   "License"); you may not use this file except in compliance
   with the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing,
   software distributed under the License is distributed on an
   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   KIND, either express or implied.  See the License for the
   specific language governing permissions and limitations
   under the License.

.. include:: ../../common.defs

.. highlight:: cpp
.. default-domain:: cpp

.. _JSONRPC: https://www.jsonrpc.org/specification
.. _JSON: https://www.json.org/json-en.html


.. |str| replace:: ``string``
.. |arraynum| replace:: ``array[number]``
.. |arraystr| replace:: ``array[string]``
.. |num| replace:: *number*
.. |strnum| replace:: *string|number*
.. |object| replace:: *object*
.. |array| replace:: *array*
.. |optional| replace:: ``optional``
.. |arrayrecord| replace:: ``array[record]``
.. |arrayerror| replace:: ``array[errors]``
.. |RPC| replace:: JSONRPC 2.0

Architecture
************


Protocol
========

The RPC mechanism implements the  `JSONRPC`_ protocol. You can refer to this section `jsonrpc-protocol`_ for more information.

Server
======

.. _jsonrpc-architecture-ipc:

IPC
---

The current server implementation runs on an IPC Socket(Unix Domain Socket). This server implements an iterative server style.
The implementation runs on a dedicated ``TSThread`` and as their style express, this performs blocking calls to all the registered handlers.
Configuration for this particular server style can be found in the admin section :ref:`admin-jsonrpc-configuration`.


Using the JSONRPC mechanism
===========================

As a user, currently,  :program:`traffic_ctl` exercises this new protocol, please refer to the :ref:`traffic_ctl_jsonrpc` section.

As a developer, please refer to the :ref:`jsonrpc_development` for a more detailed guide.



JSON Parsing
============

Our JSONRPC  protocol implementation uses lib yamlcpp for parsing incoming and outgoing requests,
this allows the server to accept either JSON or YAML format messages which then will be parsed by the protocol implementation. This seems handy
for user that want to feed |TS| with existing yaml configuration without the need to translate yaml into json.

.. note::

   :program:`traffic_ctl` have an option to read files from disc and push them into |TS| through the RPC server. Files should be a
   valid `JSONRPC`_ message. Please check `traffic_ctl rpc` for more details.


In order to programs communicate with |TS| , This one implements a simple RPC mechanism to expose all the registered API handlers.

You can check all current API by:

   .. code-block:: bash

      traffic_ctl rpc get-api

or by using the ``show_registered_handlers`` API method.


.. _jsonrpc-protocol:

JSONRPC 2.0 Protocol
====================

JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol. Primarily this specification defines several data structures
and the rules around their processing. It is transport agnostic in that the concepts can be used within the same process, over sockets,
over http, or in many various message passing environments. It uses JSON (RFC 4627) as data format.

Overview
========

.. note::

   Although most of the protocol specs are granted, we have implemented some exceptions. All the modifications will be properly documented.


There are a set  of mandatory fields that must be included in a `JSONRPC`_ message as well as some optional fields, all this is documented here,
you also can find this information in the `JSONRPC`_ link.

.. _jsonrpc-request:

Requests
--------

Please find the `jsonrpc 2.0 request` schema for reference ( `mgmt/rpc/schema/jsonrpc_request_schema.json` ).

* Mandatory fields.


   ============ ====== =======================================================================================
   Field        Type   Description
   ============ ====== =======================================================================================
   ``jsonrpc``  |str|  Protocol version. |TS| follows the version 2.0 so this field should be only ``2.0``
   ``method``   |str|  Method name that is intended to be invoked.
   ============ ====== =======================================================================================


* Optional parameters:


   * ``params``:

      A Structured value that holds the parameter values to be used during the invocation of the method. This member
      may be omitted. If passed then a parameters for the rpc call must be provided as a Structured value.
      Either by-position through an Array or by-name through an Object.

      #. ``by-position`` |array|

         params must be an ``array``, containing the values in the server expected order.


         .. code-block:: json

            {
               "params": [
                  "apache", "traffic", "server"
               ]
            }


         .. code-block:: json

            {
               "params": [
                  1, 2, 3, 4
               ]
            }


         .. code-block:: json

            {
               "params": [{
                  "name": "Apache"
               },{
                  "name": "Traffic"
               },{
                  "name": "Server"
               }]
            }

      #. ``by-name``: |object|

         Params must be an ``object``, with member names that match the server expected parameter names.
         The absence of expected names may result in an error being generated by the server. The names must
         match exactly, including case, to the method's expected parameters.

         .. code-block:: json

            {
               "params": {
                  "name": "Apache"
               }
            }

   * ``id``: |str|.

      An identifier established by the Client. If present, the request will be treated as a jsonrpc method and a
      response should be expected from the server. If it is not present, the server will treat the request as a
      notification and the client should not expect any response back from the server.
      *Although a |number| can  be specified here we will convert this internally to a |str|. The response will be a |str|.*


.. note::

   The |RPC| protocol supports batch requests so, multiple independent requests can be send to the server in a single json message. Batch
   request are basically an array of the above request, simply enclose them as an array ``[ .. ]``. The response for batch requests will be also
   in a form of an array.

.. _jsonrpc-response:

Responses
---------

Although each individual API call will describe the response details and some specific errors, in this section we will describe a high
level protocol response, some defined by the `JSONRPC`_ specs and some by |TS|

Please find the `jsonrpc 2.0 response` schema for reference( `mgmt/rpc/schema/jsonrpc_response_schema.json` ).



The responses have the following structure:


   ============ ======== ==============================================
   Field        Type     Description
   ============ ======== ==============================================
   ``jsonrpc``  |strnum| A Number that indicates the error type that occurred.
   ``result``            Result of the invoked operation. See `jsonrpc-result`_
   ``id``       |strnum| It will be the same as the value of the id member in the `jsonrpc-request`_ .
                         We will not be using `null` if the `id` could not be fetch from the request,
                         in that case the field will not be set.
   ``error``    |object| Error object, it will be present in case of an error. See `jsonrpc-error`_
   ============ ======== ==============================================

Example 1:

Request

   .. code-block:: json

      {
         "jsonrpc": "2.0",
         "result": ["hello", 5],
         "id": "9"
      }


Response

   .. code-block:: json

      {
         "jsonrpc":"2.0",
         "error":{
            "code":5,
            "message":"Missing method field"
         },
         "id":"9"
      }


As the protocol specifies |TS| have their own set of error, in the example above it's clear that the incoming request is missing
the method name, which |TS| sends a clear response error back.

.. _jsonrpc-result:

Result
------


* This member is required and will be present on success.
* This member will not exist if there was an error invoking the method.
* The value of this member is determined by the method invoked on the Server.

In |TS| a RPC method that does not report any error and have nothing to send back to the client will use the following format to
express that the call was successfully handled and the command was executed.


.. _success_response:


Example:

   .. code-block:: json
      :emphasize-lines: 4

      {
         "id": "89fc5aea-0740-11eb-82c0-001fc69cc946",
         "jsonrpc": "2.0",
         "result": "success"
      }

``"result": "success"`` will be set.

.. _jsonrpc-error:

Errors
------

The specs define the error fields that the client must expect to be sent back from the Server in case of any error.


=============== ======== ==============================================
Field           Type     Description
=============== ======== ==============================================
``code``        |num|    A Number that indicates the error type that occurred.
``message``     |str|    A String providing a short description of the error.
``data``        |object| This is an optional field that contains additional error data. Depending on the API this could contain data.
=============== ======== ==============================================

# data.

This can be used for nested error so |TS| can inform a detailed error.

   =============== ======== ==============================================
   Field           Type     Description
   =============== ======== ==============================================
   ``code``        |str|    The error code. Integer type.
   ``message``     |str|    The explanatory string for this error.
   =============== ======== ==============================================




Examples:

# Fetch a config record response from |TS|

Request:

   .. code-block:: json

      {
         "id":"0f0780a5-0758-4f51-a177-752facc7c0eb",
         "jsonrpc":"2.0",
         "method":"admin_lookup_records",
         "params":[
            {
               "record_name":"proxy.config.diags.debug.tags",
               "rec_types":[
                  "1",
                  "16"
               ]
            }
         ]
      }

Response:

   .. code-block:: json

      {
         "jsonrpc":"2.0",
         "result":{
            "recordList":[
               {
                  "record":{
                     "record_name":"proxy.config.diags.debug.tags",
                     "record_type":"3",
                     "version":"0",
                     "raw_stat_block":"0",
                     "order":"423",
                     "config_meta":{
                        "access_type":"0",
                        "update_status":"0",
                        "update_type":"1",
                        "checktype":"0",
                        "source":"3",
                        "check_expr":"null"
                     },
                     "record_class":"1",
                     "overridable":"false",
                     "data_type":"STRING",
                     "current_value":"rpc",
                     "default_value":"http|dns"
                  }
               }
            ]
         },
         "id":"0f0780a5-0758-4f51-a177-752facc7c0eb"
      }


# Getting errors from |TS|

Request an invalid record (invalid name)

   .. code-block:: json

      {
         "id":"f212932f-b260-4f01-9648-8332200524cc",
         "jsonrpc":"2.0",
         "method":"admin_lookup_records",
         "params":[
            {
               "record_name":"invalid.record",
               "rec_types":[
                  "1",
                  "16"
               ]
            }
         ]
      }


Response:

   .. code-block:: json

      {
         "jsonrpc":"2.0",
         "result":{
            "errorList":[
               {
                  "code":"2000",
                  "record_name":"invalid.record"
               }
            ]
         },
         "id":"f212932f-b260-4f01-9648-8332200524cc"
      }


Parse Error from an incomplete request

   .. code-block::

      {[[ invalid json


   .. code-block:: json

      {
         "jsonrpc":"2.0",
         "error":{
            "code":-32700,
            "message":"Parse error"
         }
      }



Invalid method invocation.

Request:

   .. code-block:: json

      {
         "id":"f212932f-b260-4f01-9648-8332200524cc",
         "jsonrpc":"2.0",
         "method":"some_non_existing_method",
         "params":{

         }
      }

Response:

   .. code-block::json

      {
         "error": {
            "code": -32601,
            "message": "Method not found"
         },
         "id": "ded7018e-0720-11eb-abe2-001fc69cc946",
         "jsonrpc": "2.0"
      }


.. information:

   According to the |RPC| specs, if you get an error, the ``result`` field will not be set. |TS| will grant this.



Development Guide
=================

* For details on how to implement JSONRPC  handler and expose them through the rpc server, please refer to :ref:`jsonrpc_development`.
* If you need to call a new JSONRPC API through :program:`traffic_ctl`, please refer to :ref:`developer-guide-traffic_ctl-development`
* To interact directly with the |RPC| node, please check :ref:`jsonrpc-node`

See also
========

:ref:`admin-jsonrpc-configuration`,
:ref:`jsonrpc-node-errors`,
:ref:`traffic_ctl_jsonrpc`
