113 lines
3.0 KiB
V
113 lines
3.0 KiB
V
module openrpc
|
|
|
|
import json
|
|
import x.json2
|
|
import net.unix
|
|
import os
|
|
import freeflowuniverse.herolib.ui.console
|
|
import freeflowuniverse.herolib.schemas.jsonrpc
|
|
|
|
pub struct UNIXServer {
|
|
pub mut:
|
|
listener &unix.StreamListener
|
|
socket_path string
|
|
handler Handler @[required]
|
|
}
|
|
|
|
@[params]
|
|
pub struct UNIXServerParams {
|
|
pub mut:
|
|
socket_path string = '/tmp/heromodels'
|
|
}
|
|
|
|
pub fn start_unix_server(handler Handler, params UNIXServerParams) ! {
|
|
mut server := new_unix_server(handler, params)!
|
|
server.start()!
|
|
}
|
|
|
|
pub fn new_unix_server(handler Handler, params UNIXServerParams) !&UNIXServer {
|
|
// Remove existing socket file if it exists
|
|
if os.exists(params.socket_path) {
|
|
os.rm(params.socket_path)!
|
|
}
|
|
|
|
listener := unix.listen_stream(params.socket_path, unix.ListenOptions{})!
|
|
|
|
return &UNIXServer{
|
|
listener: listener
|
|
handler: handler
|
|
socket_path: params.socket_path
|
|
}
|
|
}
|
|
|
|
pub fn (mut server UNIXServer) start() ! {
|
|
console.print_header('Starting HeroModels OpenRPC Server on ${server.socket_path}')
|
|
|
|
for {
|
|
mut conn := server.listener.accept()!
|
|
spawn server.handle_connection(mut conn)
|
|
}
|
|
}
|
|
|
|
pub fn (mut server UNIXServer) close() ! {
|
|
server.listener.close()!
|
|
if os.exists(server.socket_path) {
|
|
os.rm(server.socket_path)!
|
|
}
|
|
}
|
|
|
|
fn (mut server UNIXServer) handle_connection(mut conn unix.StreamConn) {
|
|
defer {
|
|
conn.close() or { console.print_stderr('Error closing connection: ${err}') }
|
|
}
|
|
|
|
for {
|
|
// Read JSON-RPC request
|
|
mut buffer := []u8{len: 4096}
|
|
bytes_read := conn.read(mut buffer) or {
|
|
console.print_debug('Connection closed or error reading: ${err}')
|
|
break
|
|
}
|
|
|
|
if bytes_read == 0 {
|
|
break
|
|
}
|
|
|
|
request_data := buffer[..bytes_read].bytestr()
|
|
console.print_debug('Received request: ${request_data}')
|
|
|
|
// Process the JSON-RPC request
|
|
if response := server.process_request(request_data) {
|
|
// Send response only if we have a valid response
|
|
conn.write_string(response) or {
|
|
console.print_stderr('Error writing response: ${err}')
|
|
break
|
|
}
|
|
} else {
|
|
// Log the error but don't break the connection
|
|
// According to JSON-RPC 2.0 spec, if we can't decode the request ID,
|
|
// we should not send any response but keep the connection alive
|
|
console.print_debug('Invalid request received, no response sent: ${err}')
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut server UNIXServer) process_request(request_data string) ?string {
|
|
// Parse JSON-RPC request using json2 to handle Any types
|
|
request := jsonrpc.decode_request(request_data) or {
|
|
// try decoding id to give error response
|
|
if id := jsonrpc.decode_request_id(request_data) {
|
|
// We can extract ID, so return proper JSON-RPC error response
|
|
return jsonrpc.new_error(id, jsonrpc.invalid_request).encode()
|
|
} else {
|
|
// Cannot extract ID from invalid JSON - return none (no response)
|
|
// This follows JSON-RPC 2.0 spec: no response when ID cannot be determined
|
|
return none
|
|
}
|
|
}
|
|
response := server.handler.handle(request) or {
|
|
return jsonrpc.new_error(request.id, jsonrpc.internal_error).encode()
|
|
}
|
|
return response.encode()
|
|
}
|