The following is pre-writing for a new Chapter in Leo's documentation.
The following is pre-writing for a new Chapter in Leo's documentation.All comments, suggestions, additions, and corrections are welcome.-----
leoserver.py is a stand-alone web server that provides access to all of Leo's capabilities using Leo's bridge. leoserver.py is not part of Leo's core.
leoclient.py is an example client, written in python. Clients may be written in any language. Clients send text requests to the server, in JSON format, and receive JSON responses from the server.
This chapter describes how to start the server and the communication protocol, that is, the format of the JSON requests and responses.Starting the serverTo start the server, execute `python leoserver.py <args>` in a separate process, that is, outside of Leo itself. `leoserver -h` prints "leoserver.py -a <address> -p <port>"By default, the server listens on "localhost" and port 32125. You can change these defaults with the --address and --port command-line arguments.Communication protocol: requests
The _do_message method expects that incoming requests are JSON objects that can be converted to a python dict, with the following three keys:"id": A positive integer."action": A string, which is either:
- The name of public method of this class, prefixed with a '!'.
- The name of a leo command, without prefix, to be run by _do_leo_command."param": A dict to be passed to the called "action" method.
Communication protocol: requests
The _make_response and _make_minimal_response methods return python dicts that are then converted do JSON. All responses contain an 'id' key that matches the corresponding requests.
Archived positionsLeo's Position objects can not be converted directly to JSON. Instead, requests and response contain archived positions, that are strings representing positions. The server's _ap_to_p and _p_to_ap methods convert between real Leo Positions and archived positions.Technical detailsThe ws_handler function contains the main server loop. It calls Python's asyncio.get_event_loop() to start listening on the designated socket and port.
ws_handler dispatches incoming requests to the _do_request method, which then dispatches the requests to the appropriate handler, either in the LeoServer class, or (via Leo's bridge) to Leo itself.
Exception handling
Request handlers raise ServerError exceptions for badly formatted user requests. The server returns an response describing the error.
The server raises InternalServerError for errors within the server itself. The server prints an error message and stops.Request handlers raise TerminateServer to terminate the server normally.Compatibility and future directionsThe present version of leoserver.py exists to support leoInteg: Leo hosted on vs code.In future, features (request handlers) can be added to leoserver.py, but features probably will never be removed.AcknowledgementsFélix Malboeuf wrote the original version of leoserver.py including the first draft of the all-important The ws_handler function. Edward K. Ream helped make the code more pythonic, and provided Leo-specific advice.
Looking at this and thinking about it a bit for the first time, I have the reaction that an HTTP server should respond to at least GET and POST requests.
Looking at this and thinking about it a bit for the first time, I have the reaction that an HTTP server should respond to at least GET and POST requests. The draft docs say nothing about this. The server docs should at least explain how to send a request to the server. It should not be left to the sample client. And if there is to be a web server, it would be useful for it to serve one or a few actual web pages, which could in fact contain the documentation for using it. Tomcat does this by default, for example.As an example of what I mean, the draft doc says"The **_do_message** method expects that incoming requests are JSON object ..."
After reading the source a bit, it became clear to me, that it's not intended to offer an HTTP(s) [1] interface, but its providing a WS(S) [2] interface.If I understand it correctly, this is at a much lower / generic level.
I'm not necessarily saying that it doesn't do what it's supposed to. I'm saying that the draft doc doesn't say what it is supposed to do, at least not so an inexperienced user can do anything with it.
QQQ
leoserver.py is a stand-alone web server that provides access to Leo’s capabilities using Leo’s bridge.
leoserver.py exists to support leoInteg: Leo hosted on Visual Studio Code.
QQQ
leoserver.py is not for inexperienced users. Imo, the above description does describe what the server does, and is supposed to do :-)
I'm saying that this draft doc doesn't tell the reader what and how it's doing those things. OK, it says to run it with parameters. But it doesn't say how to compose a message nor how to receive one, nor which of those **_do_message things are supported.
Because it was documented as not being part of core and I was not familiarized with the code of recent years, I was also initially confused by this file[1], residing in /external/leoserver/ -- an older development, which is a web (http) server.
This leoserver.py is a web socket server[2] (probably should have been explicit in the doc from the start), and is located in /core/ despite not being part of core, and isn't yet part of devel.
New implementations through this server are going to be interesting projects.
For now, I suppose the key to understanding the payloads will be leoserver.py, leoclient.py[3], and currently implemented leoInteg[4].
I'm not necessarily saying that it doesn't do what it's supposed to. I'm saying that the draft doc doesn't say what it is supposed to do, at least not so an inexperienced user can do anything with it.
At present (I've just pushed some changes to the ekr-docs branch), the first two sentences of the new chapter are:
QQQ
leoserver.py is a stand-alone web **socket** server that provides access to Leo’s capabilities using Leo’s bridge.
leoserver.py exists to support leoInteg: Leo hosted on Visual Studio Code.
QQQ
leoserver.py is not for inexperienced users. Imo, the above description does describe what the server does, and is supposed to do :-)
To address Viktor's last point: Both the original, and the new one, are made to be totally agnostic of the client and are not made with leointeg exclusivity in mind at all. Any signs of that not being the case are/were accidental, and are being corrected. (it's true that sometimes the nomenclature of some json object members matched vscode terminology instead of Leo's. For example, in Leo we refer to the current cursor locations as the cursor 'insert' point. not the 'active' point like in vscode. This was accidental and not intended)So yes, it may have been created at first to support leointeg, but it exists to support any and all other web-socket GUI client that may (or will) exist. And that's why Edward and I think it should be part of Leo-Editor and not leointeg.
On Sat, May 15, 2021 at 10:52 AM Alexander <al...@possente.net> wrote:Thanks for these comments.Because it was documented as not being part of core and I was not familiarized with the code of recent years, I was also initially confused by this file[1], residing in /external/leoserver/ -- an older development, which is a web (http) server.leo/external/leoserver.py no longer exists.