Skip to content

Commit

Permalink
Merge pull request #6 from flying-dice/docs
Browse files Browse the repository at this point in the history
docs: improve docs and example
  • Loading branch information
JonathanTurnock authored Jan 18, 2025
2 parents 5b55be2 + a4dc800 commit 0532bc3
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 85 deletions.
139 changes: 72 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# JSON-RPC Server with Lua Integration

This project provides a fully functional JSON-RPC server implemented in Rust with integration to Lua.
It supports both HTTP and WebSocket endpoints for receiving and responding to JSON-RPC requests.
This project provides a fully functional JSON-RPC server implemented in Rust with integration to Lua.
It supports both HTTP and WebSocket endpoints for receiving and responding to JSON-RPC requests.

The RPC Router and Request Handler is extended through Lua scripts, making it highly customizable and embeddable.

Expand All @@ -10,43 +10,55 @@ The RPC Router and Request Handler is extended through Lua scripts, making it hi
- **JSON-RPC Support**: Implements JSON-RPC 2.0 protocol.
- **Dual Endpoint**: Supports both HTTP and WebSocket endpoints for RPC communication.
- **Lua Integration**: Allows Lua scripts to dynamically handle and process RPC requests.
- **Asynchronous Processing**: Uses Actix and Tokio for handling asynchronous requests.
- **Session Management**: Manages WebSocket sessions and broadcasts responses to all active sessions.
- **Asynchronous Processing**: Uses Actix accepting requests and conversion to and from JSON

## How It Works

### 1. Lua Integration

The server exposes functions to Lua, allowing Lua scripts to start the server, process incoming RPC requests, and encode/decode JSON data. Here's an overview of the Lua functions provided:
The server exposes functions to Lua, allowing Lua scripts to start the server, process incoming RPC requests, and
encode/decode JSON data. Here's an overview of the Lua functions provided:

- `start_server(port: number)`: Starts the server on the specified port.
- `start_server(config: AppConfig)`: Starts the server on the specified port
- `process_rpc(callback: function)`: Processes incoming RPC requests by calling the specified Lua callback function.
- `encode(value: table)`: Encodes a Lua table into a JSON string.
- `decode(json: string)`: Decodes a JSON string into a Lua table.

#### App Config

The `AppConfig` struct is used to configure the server. It includes the following fields:

- `host`: The host address to bind the server to.
- `port`: The port number to listen on.
- `workers`: The number of worker threads to use for processing requests.
- `api_key`: The API key required to access the server. (Optional)

The API key needs to be included in the request headers as `x-api-key` to authenticate the request.

### 2. Example Lua Usage

Here’s a sample Lua script to start the server and handle incoming RPC requests:

```lua
package.path = package.path .. ";.C:\\PATH\\TO\\DLL\\?.lua"
package.cpath = package.cpath .. ";C:\\PATH\\TO\\DLL\\?.dll"
local json_rpc = require("json_rpc_server")

-- Start the server on port 1234
json_rpc.start_server(1234)

-- Define the RPC handler
-- Payloads are JSON-RPC requests in string format
function on_rpc(payload)
local request = json_rpc.decode(payload) -- {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": "1"}

print("Routing Request: " .. request.method)

if (request.id == nil) then -- Used for handlers that don't require a response
return
end

-- MAKE SURE YOUR WORKING DIRECTORY IS THE ROOT OF THE PROJECT NOT `src` OR ANY OTHER FOLDER
package.path = package.path .. ";.\\target\\debug\\?.lua"
package.cpath = package.cpath .. ";.\\target\\debug\\?.dll"

local jsonrpc = require("lua_json_rpc")

local stop = jsonrpc.start_server({
host = "0.0.0.0",
port = 1359,
workers = 2,
api_key = "super-secret-k3y"
})

io.write("JSON-RPC server started on port 1359\n")
io.flush()

function on_rpc(request)
io.write("Routing Request: " .. request.method .. "\n")

local response = {
id = request.id,
jsonrpc = "2.0",
Expand All @@ -58,34 +70,23 @@ function on_rpc(payload)

io.flush()

-- Return the response as a JSON string
return json_rpc.encode(response) -- {"id":"1","jsonrpc":"2.0","result":19}
return response
end

-- Continuously process incoming RPC requests
while true do
json_rpc.process_rpc(on_rpc)
end
```
local started = os.clock()

## Usage

Download Rust: https://www.rust-lang.org/

### Running the Server
---- Run for 10 seconds
while os.clock() - started < 30 do
jsonrpc.process_rpc(on_rpc)
end

1. **Build the Rust Library**:
Compile the Rust code to generate a shared library (`.dll`, `.so`, or `.dylib`) for Lua to load.
print("Shutting down JSON-RPC server")
stop()

```shell
cargo build --release
os.execute("echo Press any key to continue... && pause > nul")
```

2. **Configure Lua Script**:
Update the `package.path` and `package.cpath` in your Lua script to point to the generated shared library.

3. **Run the Lua Script**:
Use a Lua interpreter to run your Lua script and start the server.
## Usage

### Sending Requests

Expand All @@ -109,20 +110,23 @@ curl -X POST http://localhost:1234/rpc \

```json
{
"jsonrpc": "2.0",
"method": "subtract",
"params": [42, 23],
"id": "1"
"jsonrpc": "2.0",
"method": "subtract",
"params": [
42,
23
],
"id": "1"
}
```

### Expected Response

```json
{
"jsonrpc": "2.0",
"id": "1",
"result": 19
"jsonrpc": "2.0",
"id": "1",
"result": 19
}
```

Expand All @@ -132,26 +136,26 @@ curl -X POST http://localhost:1234/rpc \
- If the request is valid but no response is generated, a `202 Accepted` with OK is returned.
- If the request is a websocket request but no response is required (e.g., notifications), no response is sent.

## Building and Running
## Building

1. **Clone the Repository**:
The project can be built using Cargo. Run the following command in the project root directory:

```bash
git clone <repository-url>
cd json-rpc-server
```
```bash
cargo build
```

This will build the project in debug mode. To build in release mode, run:

2. **Build the Rust Project**:
```bash
cargo build --release
```

```bash
cargo build --release
```
By default the build will produce a DLL compatible with the lua runtime installed on the host.

3. **Run the Lua Script**:
The build is set up for DCS world currently as such the lua5.1 directory is provided to ensure compatibility with DCS
world.

```bash
lua main.lua
```
Run `build-dcs.bat` to build the project for DCS world creating a compatible DLL.

## Contributing

Expand All @@ -163,4 +167,5 @@ This project is licensed under the MIT License. See the [LICENSE.md](LICENSE.md)

## Acknowledgements

Special thanks to the contributors of Actix, mlua, and the Rust and Lua communities for their excellent libraries and support.
Special thanks to the contributors of Actix, mlua, and the Rust and Lua communities for their excellent libraries and
support.
20 changes: 2 additions & 18 deletions main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,16 @@ local jsonrpc = require("lua_json_rpc")
local stop = jsonrpc.start_server({
host = "0.0.0.0",
port = 1359,
workers = 2
workers = 2,
api_key = "super-secret-k3y"
})

io.write("JSON-RPC server started on port 1359\n")
io.flush()

function dump(o)
if type(o) == 'table' then
local s = '{ '
for k, v in pairs(o) do
if type(k) ~= 'number' then
k = '"' .. k .. '"'
end
s = s .. '[' .. k .. '] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end

function on_rpc(request)
io.write("Routing Request: " .. request.method .. "\n")

io.write(dump(request) .. "\n")

local response = {
id = request.id,
jsonrpc = "2.0",
Expand Down

0 comments on commit 0532bc3

Please sign in to comment.