Compare commits

..

6 Commits

Author SHA1 Message Date
710f5f197f Make a release, create NEWS 2020-09-05 20:42:02 +02:00
ba68585d14 Streamline the manual page a bit
I have consulted `man 7 man-pages` but overall it's a huge mess.
2020-09-05 06:34:00 +02:00
984e5b4e7f Use saner defaults
So that most of the time users won't need to use any switches.

--pretty-print has been inverted into jq's --compact-output,
and --auto-id has been replaced with barely, if-at-all useful
--null-as-id.
2020-09-05 06:07:45 +02:00
d57a8bd3c7 Improve AsciiDoc compatibility
I need two renderers to work: hswg/libasciidoc and asciidoctor
in man page mode (and ideally in HTML as well).  That should be
covered now.

The triple-plus thing was the first thing that showed good results,
after trying backslashes, single-plus quoting and [] after ://.

The change of the source code block kind could be considered as
unification.  I'm combining tabs with spaces within one document
though, and I should get rid of the tabs in the rest of it then...
2020-09-05 04:36:43 +02:00
2962a644da Write a nice new man page in AsciiDoc
Taking some preliminary steps for inclusion in Linux distributions.

The help message has been slightly improved and the README extended,
with part of it now residing in the man page.

One less GNU dependency, for what it's worth.
2020-09-05 03:51:36 +02:00
6f5ef30293 Move "connecting..." messages to the debug mode
So that the --verbose option does only one thing.
2020-09-05 03:50:14 +02:00
5 changed files with 230 additions and 47 deletions

View File

@@ -13,8 +13,8 @@ if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
# Version
set (project_VERSION_MAJOR "0")
set (project_VERSION_MINOR "1")
set (project_VERSION_MAJOR "1")
set (project_VERSION_MINOR "0")
set (project_VERSION_PATCH "0")
set (project_VERSION "${project_VERSION_MAJOR}")
@@ -87,18 +87,20 @@ install (PROGRAMS json-format.pl DESTINATION ${CMAKE_INSTALL_BINDIR})
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
# Generate documentation from program help
find_program (HELP2MAN_EXECUTABLE help2man)
if (NOT HELP2MAN_EXECUTABLE)
message (FATAL_ERROR "help2man not found")
endif (NOT HELP2MAN_EXECUTABLE)
find_program (ASCIIDOCTOR_EXECUTABLE asciidoctor)
if (NOT ASCIIDOCTOR_EXECUTABLE)
message (FATAL_ERROR "asciidoctor not found")
endif (NOT ASCIIDOCTOR_EXECUTABLE)
foreach (page ${PROJECT_NAME})
set (page_output "${PROJECT_BINARY_DIR}/${page}.1")
list (APPEND project_MAN_PAGES "${page_output}")
add_custom_command (OUTPUT ${page_output}
COMMAND ${HELP2MAN_EXECUTABLE} -N
"${PROJECT_BINARY_DIR}/${page}" -o ${page_output}
DEPENDS ${page}
COMMAND ${ASCIIDOCTOR_EXECUTABLE} -b manpage
-a release-version=${project_VERSION}
"${PROJECT_SOURCE_DIR}/${page}.adoc"
-o "${page_output}"
DEPENDS ${page}.adoc
COMMENT "Generating man page for ${page}" VERBATIM)
endforeach (page)
@@ -111,7 +113,8 @@ foreach (page ${project_MAN_PAGES})
endforeach (page)
# CPack
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Shell for running JSON-RPC 2.0 queries")
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY
"A shell for running JSON-RPC 2.0 queries")
set (CPACK_PACKAGE_VENDOR "Premysl Eric Janouch")
set (CPACK_PACKAGE_CONTACT "Přemysl Eric Janouch <p@janouch.name>")
set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")

4
NEWS Normal file
View File

@@ -0,0 +1,4 @@
1.0.0 (2020-09-05)
* Initial release

View File

@@ -17,38 +17,28 @@ you get the following niceties:
- ability to pipe output through a shell command, so that you can view the
results in your favourite editor or redirect them to a file
- ability to edit the input line in your favourite editor as well with Alt+E
- WebSockets (RFC 6455) can also be used as a transport rather than HTTP
Supported transports
--------------------
- HTTP
- HTTPS
- WebSocket
- WebSocket over TLS
WebSockets
~~~~~~~~~~
The JSON-RPC 2.0 spec doesn't say almost anything about underlying transports.
The way it's implemented here is that every request is sent as a single text
message. If it has an "id" field, i.e. it's not just a notification, the
client waits for a message from the server in response.
There's no support so far for any protocol extensions, nor for specifying
the higher-level protocol (the "Sec-Ws-Protocol" HTTP field).
Documentation
-------------
See the link:json-rpc-shell.adoc[man page] for information about usage.
The rest of this README will concern itself with externalities.
Packages
--------
Regular releases are sporadic. git master should be stable enough. You can get
a package with the latest development version from Archlinux's AUR.
Building and Usage
------------------
Build dependencies: CMake, pkg-config, help2man,
Building
--------
Build dependencies: CMake, pkg-config, asciidoctor,
liberty (included), http-parser (included) +
Runtime dependencies: libev, Jansson, cURL, openssl,
readline or libedit >= 2013-07-12,
Avoid libedit if you can, in general it works but at the moment history is
acting up and I have no clue about fixing it.
acting up and I have no clue about fixing it. Multiline editing is also
misbehaving there.
$ git clone --recursive https://git.janouch.name/p/json-rpc-shell.git
$ mkdir json-rpc-shell/build
@@ -68,13 +58,12 @@ Or you can try telling CMake to make a package for you. For Debian it is:
Note that for versions of CMake before 2.8.9, you need to prefix `cpack` with
`fakeroot` or file ownership will end up wrong.
Run the program with `--help` to obtain usage information.
Test server
-----------
If you install development packages for libmagic, an included test server will
be built but not installed which provides a trivial JSON-RPC 2.0 service with
FastCGI, SCGI, and WebSocket interfaces. It responds to the `ping` method.
FastCGI, SCGI, and WebSocket interfaces. It responds to `ping` and `date`
methods and it can serve static files.
Contributing and Support
------------------------

180
json-rpc-shell.adoc Normal file
View File

@@ -0,0 +1,180 @@
json-rpc-shell(1)
=================
:doctype: manpage
:manmanual: json-rpc-shell Manual
:mansource: json-rpc-shell {release-version}
Name
----
json-rpc-shell - a simple JSON-RPC 2.0 shell
Synopsis
--------
*json-rpc-shell* [_OPTION_]... _ENDPOINT_
Description
-----------
The _ENDPOINT_ must be either an HTTP or a WebSocket URL, with or without TLS
(i.e. one of the _+++http+++://_, _+++https+++://_, _ws://_, _wss://_ schemas).
*json-rpc-shell* will use it to send any JSON-RPC 2.0 requests you enter on its
command line. The server's response will be parsed and validated, stripping it
of the protocol's noisy envelope. At your option, it can then also be
pretty-printed, rendered with adjustable syntax highlighting, or even piped
through another program such as the *less*(1) pager or the *jq*(1) JSON
processor.
Usage
~~~~~
Three things may appear on the internal command line, in a sequence. The first
one is always the name of the JSON-RPC method to call, as a bare word, separated
from the rest by white space. Following that, you may enter three kinds of JSON
values. If it is an object or an array, it constitutes the method parameters.
If it is a string or a number, it is taken as the "id" to use for the request,
which would be chosen for you automatically if left unspecified. Finally,
a null value indicates that the request should be sent as a notification,
lacking the ID completely. Booleans cannot be used for anything.
The response to the method call may be piped through external commands, the same
way you would do it in a Unix shell.
Exit the program by pressing C-c or C-d. No special keywords are reserved for
this action as they might conflict with method names.
Options
-------
Controlling output
~~~~~~~~~~~~~~~~~~
*-c*, *--compact-output*::
Do not pretty-print responses. Normally, spaces and newlines are added
where appropriate to improve readability.
*--color*=_WHEN_::
By default, when the output of the program is a terminal, JSON responses
are syntax-highlighted. This corresponds to the _auto_ setting. You may
also set this to _always_ or _never_. In either case, color is never
applied when piping to another program.
*-v*, *--verbose*::
Print raw requests and responses, including the JSON-RPC 2.0 envelope.
*-d*, *--debug*::
Print even more information to help debug various issues.
Protocol
~~~~~~~~
*-n*, *--null-as-id*::
Normally, entering a null JSON value on the command line causes
a notification to be sent. With this option, it is sent as the "id"
field of a normal request, which is discouraged by the specification.
*-t*, *--trust-all*::
Trust all SSL/TLS certificates. Useful in case that the certificate is
self-signed, or when the CA isn't in your CA store. Beware that this option
is about as good as using plain unencrypted HTTP.
*-o* _ORIGIN_, *--origin*=_ORIGIN_::
Set the HTTP Origin header to _ORIGIN_. Some servers may need this.
Program information
~~~~~~~~~~~~~~~~~~~
*-h*, *--help*::
Display a help message and exit.
*-V*, *--version*::
Output version information and exit.
*--write-default-cfg*[**=**__PATH__]::
Write a default configuration file, show its path and exit.
Files
-----
_~/.config/json-rpc-shell/json-rpc-shell.conf_::
The configuration file, in which you can configure color output and
CA certificate paths. Use the *--write-default-cfg* option to create
a new one for editing.
_~/.local/share/json-rpc-shell/history_::
All your past method invocations are stored here upon exit and loaded back
on start-up.
Notes
-----
Editing
~~~~~~~
While single-line editing on the command line may be satisfactory for simple
requests, it is often convenient or even necessary to run a full text editor
in order to construct complex objects or arrays, and may even be used to import
data from elsewhere. You can launch an editor for the current request using
the M-e key combination. Both *readline*(3) and *editline*(7) also support
multiline editing natively, though you need to press C-v C-j in order to insert
newlines.
WebSockets
~~~~~~~~~~
The JSON-RPC 2.0 specification doesn't say almost anything about underlying
transports. As far as the author is aware, he is the only person combining it
with WebSockets. The way it's implemented here is that every request is sent as
a single text message. If it has an "id" field, i.e., it's not just
a notification, the client waits for a message from the server in response.
Should any message arrive unexpectedly, you will receive a warning.
There is no support so far for any protocol extensions, nor for specifying
the higher-level protocol (the "Sec-Ws-Protocol" HTTP field).
Bugs
----
The editline (libedit) frontend is more of a proof of concept that mostly seems
to work but exhibits bugs that are not our fault.
Examples
--------
Running some queries against json-rpc-test-server, included in the source
distribution of this program (public services are hard to find):
Methods without parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~
$ json-rpc-shell ws://localhost:1234
json-rpc> ping
"pong"
json-rpc> date
{
"year": 2020,
"month": 9,
"day": 5,
"hours": 2,
"minutes": 23,
"seconds": 51
}
Notification with a parameter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Notifications never produce a response, not even when the method is not known
to the server:
$ json-rpc-shell ws://localhost:1234
json-rpc> notify {"events": ["conquest", "war", "famine", "death"]} null
[Notification]
Piping in and out
~~~~~~~~~~~~~~~~~
GNU Readline always repeats the prompt, which makes this a bit less useful
for invoking from other programs:
$ echo 'ping | jq ascii_upcase' | json-rpc-shell ws://localhost:1234
json-rpc> ping | jq ascii_upcase
"PONG"
Reporting bugs
--------------
Use https://git.janouch.name/p/json-rpc-shell to report bugs, request features,
or submit pull requests.
See also
--------
*jq*(1), *readline*(3) or *editline*(7)
Specifications
~~~~~~~~~~~~~~
https://www.jsonrpc.org/specification +
https://www.json.org

View File

@@ -932,11 +932,11 @@ static struct app_context
struct config config; ///< Program configuration
enum color_mode color_mode; ///< Colour output mode
bool pretty_print; ///< Whether to pretty print
bool compact; ///< Whether to not pretty print
bool verbose; ///< Print requests
bool trust_all; ///< Don't verify peer certificates
bool auto_id; ///< Use automatically generated ID's
bool null_as_id; ///< JSON null is used as an ID
int64_t next_id; ///< Next autogenerated ID
iconv_t term_to_utf8; ///< Terminal encoding to UTF-8
@@ -1683,7 +1683,7 @@ backend_ws_establish_connection (struct ws_context *self,
else
real_host = buf;
if (self->ctx->verbose)
if (g_debug_mode)
{
char *address = format_host_port_pair (real_host, port);
print_status ("connecting to %s...", address);
@@ -2845,7 +2845,7 @@ process_response (struct app_context *ctx, const json_t *id, struct str *buf,
if (result)
{
int flags = JSON_ENCODE_ANY;
if (ctx->pretty_print)
if (!ctx->compact)
flags |= JSON_INDENT (2);
char *utf8 = json_dumps (result, flags);
@@ -3041,9 +3041,16 @@ process_input (char *user_input, void *user_data)
*target = json_incref (args[i]);
}
if (!id && ctx->auto_id)
if (!id)
id = json_integer (ctx->next_id++);
// Use nulls to send notifications, unless a special switch is used
if (!ctx->null_as_id && json_is_null (id))
{
json_decref (id);
id = NULL;
}
make_json_rpc_call (ctx, method, id, params, pipeline);
fail_parse:
@@ -3339,14 +3346,14 @@ parse_program_arguments (struct app_context *ctx, int argc, char **argv,
static const struct opt opts[] =
{
{ 'd', "debug", NULL, 0, "run in debug mode" },
{ 'h', "help", NULL, 0, "display this help and exit" },
{ 'h', "help", NULL, 0, "display this help message and exit" },
{ 'V', "version", NULL, 0, "output version information and exit" },
{ 'a', "auto-id", NULL, 0, "automatic `id' fields" },
{ 'n', "null-as-id", NULL, 0, "JSON null is used as an `id'" },
{ 'o', "origin", "O", 0, "set the HTTP Origin header" },
{ 'p', "pretty", NULL, 0, "pretty-print the responses" },
{ 'c', "compact-output", NULL, 0, "do not pretty-print responses" },
{ 't', "trust-all", NULL, 0, "don't care about SSL/TLS certificates" },
{ 'v', "verbose", NULL, 0, "print the request before sending" },
{ 'c', "color", "WHEN", OPT_LONG_ONLY,
{ 'v', "verbose", NULL, 0, "print raw requests and responses" },
{ 'C', "color", "WHEN", OPT_LONG_ONLY,
"colorize output: never, always, or auto" },
{ 'w', "write-default-cfg", "FILENAME",
OPT_OPTIONAL_ARG | OPT_LONG_ONLY,
@@ -3355,7 +3362,7 @@ parse_program_arguments (struct app_context *ctx, int argc, char **argv,
};
struct opt_handler oh = opt_handler_make (argc, argv, opts,
"ENDPOINT", "Simple JSON-RPC shell.");
"ENDPOINT", "A simple JSON-RPC 2.0 shell.");
int c;
while ((c = opt_handler_get (&oh)) != -1)
@@ -3372,12 +3379,12 @@ parse_program_arguments (struct app_context *ctx, int argc, char **argv,
exit (EXIT_SUCCESS);
case 'o': *origin = optarg; break;
case 'a': ctx->auto_id = true; break;
case 'p': ctx->pretty_print = true; break;
case 'n': ctx->null_as_id = true; break;
case 'c': ctx->compact = true; break;
case 't': ctx->trust_all = true; break;
case 'v': ctx->verbose = true; break;
case 'c':
case 'C':
if (!strcasecmp (optarg, "never"))
ctx->color_mode = COLOR_NEVER;
else if (!strcasecmp (optarg, "always"))