fix: add client error unwrap

This commit is contained in:
Kevin Franklin Kim 2025-03-07 14:53:01 +01:00
parent f8e193bcd6
commit 07c3475cc9
No known key found for this signature in database
11 changed files with 132 additions and 4 deletions

View File

@ -9,3 +9,11 @@ func NewClientError(err error) *ClientError {
error: err,
}
}
// Unwrap interface
func (e *ClientError) Unwrap() error {
if e != nil && e.error != nil {
return e.error
}
return nil
}

View File

@ -16,6 +16,9 @@ export class ServiceClient {
async boolSlice(v:Array<boolean>|null):Promise<Array<boolean>|null> {
return (await this.transport<{0:Array<boolean>|null}>("BoolSlice", [v]))[0]
}
async context():Promise<void> {
await this.transport<void>("Context", [])
}
async empty():Promise<void> {
await this.transport<void>("Empty", [])
}

View File

@ -0,0 +1,27 @@
package main_test
import (
"context"
"net/http/httptest"
"testing"
"time"
"github.com/foomo/gotsrpc/v2/example/basic/service"
"github.com/stretchr/testify/assert"
)
func TestContextCanceled(t *testing.T) {
ctx, cancel := context.WithCancel(context.TODO())
go func() {
time.Sleep(time.Second)
cancel()
}()
svr := httptest.NewServer(service.NewDefaultServiceGoTSRPCProxy(&service.Handler{}))
defer svr.Close()
client := service.NewDefaultServiceGoTSRPCClient(svr.URL)
clientErr := client.Context(ctx)
assert.ErrorIs(t, clientErr, context.Canceled)
}

View File

@ -42,6 +42,11 @@ type (
RetBoolSlice_0 []bool
}
ServiceContextRequest struct {
}
ServiceContextResponse struct {
}
ServiceEmptyRequest struct {
}
ServiceEmptyResponse struct {
@ -461,6 +466,8 @@ func init() {
gob.Register(ServiceBoolPtrResponse{})
gob.Register(ServiceBoolSliceRequest{})
gob.Register(ServiceBoolSliceResponse{})
gob.Register(ServiceContextRequest{})
gob.Register(ServiceContextResponse{})
gob.Register(ServiceEmptyRequest{})
gob.Register(ServiceEmptyResponse{})
gob.Register(ServiceFloat32Request{})
@ -631,6 +638,9 @@ func (p *ServiceGoRPCProxy) handler(clientAddr string, request interface{}) (res
req := request.(ServiceBoolSliceRequest)
retBoolSlice_0 := p.service.BoolSlice(req.V)
response = ServiceBoolSliceResponse{RetBoolSlice_0: retBoolSlice_0}
case "ServiceContextRequest":
p.service.Context(nil, nil)
response = ServiceContextResponse{}
case "ServiceEmptyRequest":
p.service.Empty()
response = ServiceEmptyResponse{}

View File

@ -63,6 +63,16 @@ func (tsc *ServiceGoRPCClient) BoolSlice(v []bool) (retBoolSlice_0 []bool, clien
return response.RetBoolSlice_0, nil
}
func (tsc *ServiceGoRPCClient) Context() (clientErr error) {
req := ServiceContextRequest{}
_, rpcCallErr := tsc.Client.Call(req)
if rpcCallErr != nil {
clientErr = rpcCallErr
return
}
return nil
}
func (tsc *ServiceGoRPCClient) Empty() (clientErr error) {
req := ServiceEmptyRequest{}
_, rpcCallErr := tsc.Client.Call(req)

View File

@ -14,6 +14,7 @@ const (
ServiceGoTSRPCProxyBool = "Bool"
ServiceGoTSRPCProxyBoolPtr = "BoolPtr"
ServiceGoTSRPCProxyBoolSlice = "BoolSlice"
ServiceGoTSRPCProxyContext = "Context"
ServiceGoTSRPCProxyEmpty = "Empty"
ServiceGoTSRPCProxyFloat32 = "Float32"
ServiceGoTSRPCProxyFloat32Map = "Float32Map"
@ -176,6 +177,24 @@ func (p *ServiceGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request)
}
gotsrpc.Monitor(w, r, args, rets, callStats)
return
case ServiceGoTSRPCProxyContext:
var (
args []interface{}
rets []interface{}
)
executionStart := time.Now()
rw := gotsrpc.ResponseWriter{ResponseWriter: w}
p.service.Context(&rw, r)
callStats.Execution = time.Since(executionStart)
if rw.Status() == http.StatusOK {
rets = []interface{}{}
if err := gotsrpc.Reply(rets, callStats, r, w); err != nil {
gotsrpc.ErrorCouldNotReply(w)
return
}
}
gotsrpc.Monitor(w, r, args, rets, callStats)
return
case ServiceGoTSRPCProxyEmpty:
var (
args []interface{}

View File

@ -14,6 +14,7 @@ type ServiceGoTSRPCClient interface {
Bool(ctx go_context.Context, v bool) (retBool_0 bool, clientErr error)
BoolPtr(ctx go_context.Context, v bool) (retBoolPtr_0 *bool, clientErr error)
BoolSlice(ctx go_context.Context, v []bool) (retBoolSlice_0 []bool, clientErr error)
Context(ctx go_context.Context) (clientErr error)
Empty(ctx go_context.Context) (clientErr error)
Float32(ctx go_context.Context, v float32) (retFloat32_0 float32, clientErr error)
Float32Map(ctx go_context.Context, v map[float32]interface{}) (retFloat32Map_0 map[float32]interface{}, clientErr error)
@ -126,6 +127,16 @@ func (tsc *HTTPServiceGoTSRPCClient) BoolSlice(ctx go_context.Context, v []bool)
return
}
func (tsc *HTTPServiceGoTSRPCClient) Context(ctx go_context.Context) (clientErr error) {
args := []interface{}{}
reply := []interface{}{}
clientErr = tsc.Client.Call(ctx, tsc.URL, tsc.EndPoint, "Context", args, reply)
if clientErr != nil {
clientErr = pkg_errors.WithMessage(clientErr, "failed to call service.ServiceGoTSRPCProxy Context")
}
return
}
func (tsc *HTTPServiceGoTSRPCClient) Empty(ctx go_context.Context) (clientErr error) {
args := []interface{}{}
reply := []interface{}{}

View File

@ -1,10 +1,20 @@
package service
import (
"net/http"
"time"
)
type Handler struct{}
func (h *Handler) Empty() {
func (h *Handler) Context(w http.ResponseWriter, r *http.Request) {
for r.Context().Err() == nil {
time.Sleep(100 * time.Millisecond)
}
}
func (h *Handler) Empty() {}
func (h *Handler) Bool(v bool) bool {
return v
}

View File

@ -1,6 +1,11 @@
package service
import (
"net/http"
)
type Service interface {
Context(w http.ResponseWriter, r *http.Request)
Empty()
Bool(v bool) bool
BoolPtr(v bool) *bool

View File

@ -1,7 +1,9 @@
package frontend
import (
"fmt"
"net/http"
"time"
"github.com/foomo/gotsrpc/v2/example/errors/service/backend"
"github.com/foomo/gotsrpc/v2/example/errors/service/frontend"
@ -18,6 +20,12 @@ func New(client backend.ServiceGoTSRPCClient) *Handler {
}
func (h *Handler) Simple(w http.ResponseWriter, r *http.Request) (e *frontend.ErrSimple) {
fmt.Println("==========> incoming")
time.Sleep(time.Second)
if r.Context().Err() != nil {
fmt.Println("==========>" + r.Context().Err().Error())
}
fmt.Println("<========== outgoing")
return
}

View File

@ -8,7 +8,6 @@ import (
"context"
"fmt"
"net/http"
"os/exec"
"strings"
"time"
@ -40,7 +39,7 @@ func main() {
go func() {
time.Sleep(time.Second)
_ = exec.Command("open", "http://127.0.0.1:3000").Run()
// _ = exec.Command("open", "http://127.0.0.1:3000").Run()
call()
}()
@ -49,12 +48,30 @@ func main() {
func call() {
ctx := context.Background()
time.Sleep(time.Second)
ctx2, cancel := context.WithCancel(ctx)
req, _ := http.NewRequestWithContext(ctx2, "POST", "http://127.0.0.1:3000/service/frontend/Simple", nil)
go func() {
time.Sleep(time.Millisecond * 100)
fmt.Println("cancel")
cancel()
}()
fmt.Println("sending request")
res, err := http.DefaultClient.Do(req)
time.Sleep(time.Second)
fmt.Printf("%v\n", err)
fmt.Printf("%v\n", res.StatusCode)
c := backendsvs.NewDefaultServiceGoTSRPCClient("http://localhost:3000")
{
fmt.Println("--- Error ----------------------")
var gotsrpcErr *gotsrpc.Error
serviceErr, err := c.Error(ctx)
ctx2, cancel2 := context.WithCancel(ctx)
cancel2()
serviceErr, err := c.Error(ctx2)
if err != nil {
panic("client error should be nil")
} else if serviceErr == nil {