mirror of
https://github.com/hanzoai/psrpc.git
synced 2026-09-30 23:47:22 +00:00
171 lines
4.1 KiB
Go
171 lines
4.1 KiB
Go
// Copyright 2023 LiveKit, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package client
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
"github.com/livekit/psrpc"
|
|
"github.com/livekit/psrpc/internal"
|
|
"github.com/livekit/psrpc/internal/bus"
|
|
"github.com/livekit/psrpc/internal/interceptors"
|
|
"github.com/livekit/psrpc/pkg/info"
|
|
"github.com/livekit/psrpc/pkg/metadata"
|
|
"github.com/livekit/psrpc/pkg/rand"
|
|
)
|
|
|
|
func RequestMulti[ResponseType proto.Message](
|
|
ctx context.Context,
|
|
c *RPCClient,
|
|
rpc string,
|
|
topic []string,
|
|
request proto.Message,
|
|
opts ...psrpc.RequestOption,
|
|
) (rChan <-chan *psrpc.Response[ResponseType], err error) {
|
|
if c.closed.IsBroken() {
|
|
return nil, psrpc.ErrClientClosed
|
|
}
|
|
|
|
i := c.GetInfo(rpc, topic)
|
|
|
|
// request hooks
|
|
for _, hook := range c.RequestHooks {
|
|
hook(ctx, request, i.RPCInfo)
|
|
}
|
|
|
|
resChan := make(chan *psrpc.Response[ResponseType], c.ChannelSize)
|
|
m := &multiRPC[ResponseType]{
|
|
c: c,
|
|
i: i,
|
|
requestID: rand.NewRequestID(),
|
|
resChan: resChan,
|
|
}
|
|
|
|
reqInterceptors := getRequestInterceptors(
|
|
c.MultiRPCInterceptors,
|
|
getRequestOpts(ctx, i, c.ClientOpts, opts...).Interceptors,
|
|
)
|
|
m.handler = interceptors.ChainClientInterceptors[psrpc.ClientMultiRPCHandler](
|
|
reqInterceptors, i, m,
|
|
)
|
|
|
|
if err = m.handler.Send(ctx, request, opts...); err != nil {
|
|
for _, hook := range c.ResponseHooks {
|
|
hook(ctx, request, i.RPCInfo, nil, err)
|
|
}
|
|
return
|
|
}
|
|
|
|
return resChan, nil
|
|
}
|
|
|
|
type multiRPC[ResponseType proto.Message] struct {
|
|
c *RPCClient
|
|
i *info.RequestInfo
|
|
requestID string
|
|
handler psrpc.ClientMultiRPCHandler
|
|
resChan chan<- *psrpc.Response[ResponseType]
|
|
}
|
|
|
|
func (m *multiRPC[ResponseType]) Send(ctx context.Context, req proto.Message, opts ...psrpc.RequestOption) error {
|
|
o := getRequestOpts(ctx, m.i, m.c.ClientOpts, opts...)
|
|
|
|
b, err := bus.SerializePayload(req)
|
|
if err != nil {
|
|
return psrpc.NewError(psrpc.MalformedRequest, err)
|
|
}
|
|
|
|
now := time.Now()
|
|
ir := &internal.Request{
|
|
RequestId: m.requestID,
|
|
ClientId: m.c.ID,
|
|
SentAt: now.UnixNano(),
|
|
Expiry: now.Add(o.Timeout).UnixNano(),
|
|
Multi: true,
|
|
RawRequest: b,
|
|
Metadata: metadata.OutgoingContextMetadata(ctx),
|
|
}
|
|
|
|
resChan := make(chan *internal.Response, m.c.ChannelSize)
|
|
|
|
m.c.mu.Lock()
|
|
m.c.responseChannels[m.requestID] = resChan
|
|
m.c.mu.Unlock()
|
|
|
|
go m.handleResponses(ctx, req, resChan, o)
|
|
|
|
if err = m.c.bus.Publish(ctx, m.i.GetRPCChannel(), ir); err != nil {
|
|
return psrpc.NewError(psrpc.Internal, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *multiRPC[ResponseType]) handleResponses(
|
|
ctx context.Context,
|
|
req proto.Message,
|
|
resChan chan *internal.Response,
|
|
opts psrpc.RequestOpts,
|
|
) {
|
|
timer := time.NewTimer(opts.Timeout)
|
|
for {
|
|
select {
|
|
case res := <-resChan:
|
|
var v ResponseType
|
|
var err error
|
|
if res.Error != "" {
|
|
err = psrpc.NewErrorFromResponse(res.Code, res.Error, res.ErrorDetails...)
|
|
} else {
|
|
v, err = bus.DeserializePayload[ResponseType](res.RawResponse)
|
|
if err != nil {
|
|
err = psrpc.NewError(psrpc.MalformedResponse, err)
|
|
}
|
|
}
|
|
|
|
// response hooks
|
|
for _, hook := range m.c.ResponseHooks {
|
|
hook(ctx, req, m.i.RPCInfo, v, err)
|
|
}
|
|
|
|
m.handler.Recv(v, err)
|
|
|
|
case <-timer.C:
|
|
m.handler.Close()
|
|
return
|
|
|
|
case <-ctx.Done():
|
|
m.handler.Close()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *multiRPC[ResponseType]) Recv(msg proto.Message, err error) {
|
|
m.resChan <- &psrpc.Response[ResponseType]{
|
|
Result: msg.(ResponseType),
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
func (m *multiRPC[ResponseType]) Close() {
|
|
m.c.mu.Lock()
|
|
delete(m.c.responseChannels, m.requestID)
|
|
m.c.mu.Unlock()
|
|
close(m.resChan)
|
|
}
|