package rpc import ( "context" "encoding/json" "fmt" "time" "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, fmt.Errorf("Request timed out") } } // TODO Call with Multiple Response (Chunked file upload)