diff --git a/CMakeLists.txt b/CMakeLists.txt index 53acd44..09b50a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ") set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") diff --git a/README.adoc b/README.adoc index 646c93a..649a66b 100644 --- a/README.adoc +++ b/README.adoc @@ -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 ------------------------ diff --git a/json-rpc-shell.adoc b/json-rpc-shell.adoc new file mode 100644 index 0000000..20b1f8c --- /dev/null +++ b/json-rpc-shell.adoc @@ -0,0 +1,183 @@ +json-rpc-shell(1) +================= +:doctype: manpage +:man manual: json-rpc-shell Manual +:man source: 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 must always be 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 two kinds +of JSON values. If it is a string, a number, or a null value, it is taken as +the "id" to use for the request. If it is an object or an array, it constitutes +the method parameters. Booleans may appear in neither. + +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 +~~~~~~~~~~~~~~~~~~ +*-p*, *--pretty*:: + Pretty-print responses, adding spaces and newlines 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 +~~~~~~~~ +*-a*, *--auto-id*:: + Choose message IDs automatically, in an increasing sequence. Normally you + need to enter the ID on the command line manually, so as to distinguish + notifications from other requests. Even with this option enabled, you can + still specify the ID, if you wish. + +*-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_:: + 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*:: + 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): + +Pretty-printing and Manual IDs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``` +$ json-rpc-shell -p ws://localhost:1234 +json-rpc> date 1 +{ + "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"]} +[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 -a 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 diff --git a/json-rpc-shell.c b/json-rpc-shell.c index dcd0a5e..10105cd 100644 --- a/json-rpc-shell.c +++ b/json-rpc-shell.c @@ -3339,13 +3339,16 @@ 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" }, + // TODO: consider making this the default and instead adding + // an option to accept JSON null as an id. { 'a', "auto-id", NULL, 0, "automatic `id' fields" }, { 'o', "origin", "O", 0, "set the HTTP Origin header" }, + // TODO: consider inverting this to -c/--compact-output { 'p', "pretty", NULL, 0, "pretty-print the responses" }, { 't', "trust-all", NULL, 0, "don't care about SSL/TLS certificates" }, - { 'v', "verbose", NULL, 0, "print the request before sending" }, + { '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", @@ -3355,7 +3358,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)