morffix/rpc/call.go

97 lines
1.9 KiB
Go

package rpc
import (
"context"
"encoding/json"
"fmt"
"time"
"git.lastassault.de/speatzle/morffix/constants"
"github.com/google/uuid"
"nhooyr.io/websocket"
)
func (s *server) Call(ctx context.Context, c *websocket.Conn, method string, params, result any) (*Response, error) {
id := uuid.New().String()
resp := make(chan *Response, 1)
var dataParams []byte
var err error
if params != nil {
dataParams, err = json.Marshal(params)
if err != nil {
return nil, fmt.Errorf("Error Marshalling Params: %w", err)
}
}
rawParams := json.RawMessage(dataParams)
req := Request{
ID: id,
Method: method,
Params: &rawParams,
}
reqData, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf("Error Marshalling Request: %w", err)
}
// Add Call to Request Map
func() {
s.requestMutex.Lock()
defer s.requestMutex.Unlock()
s.requests[id] = resp
}()
// Remove Call from Request map
defer func() {
s.requestMutex.Lock()
defer s.requestMutex.Unlock()
delete(s.requests, id)
}()
// Write Request
err = c.Write(ctx, websocket.MessageText, reqData)
if err != nil {
return nil, fmt.Errorf("Error Writing Request: %w", err)
}
timeout := time.NewTimer(time.Second * 10)
select {
case response := <-resp:
// Cleanup timeout
if !timeout.Stop() {
<-timeout.C
}
if response.Error != nil {
return response, fmt.Errorf("Call Error (%v): %v", response.Error.Code, response.Error.Message)
}
if result == nil {
return response, nil
}
if response.Result == nil {
return response, fmt.Errorf("Got Empty Result")
}
err = json.Unmarshal(*response.Result, &result)
if err != nil {
return response, fmt.Errorf("Error Parsing Result: %w", err)
}
return response, nil
case <-timeout.C:
s.requestMutex.Lock()
defer s.requestMutex.Unlock()
// remove request from map
delete(s.requests, id)
return nil, constants.ErrRPCRequestTimeout
}
}
// TODO Call with Multiple Response (Chunked file upload)