Skip to content

Commit 03eadf8

Browse files
authored
Prevent deadlock on connection close and expose connection to the stream context (#52)
During an RPC, the generated method blocks until a response is received or the passed context is cancelled, but if the connection is closed while waiting for either event, the method remains blocked. The new generated code also listens for a connection close event, unblocking the method. Signed-off-by: Luiz Aoqui <[email protected]>
1 parent 4a6d649 commit 03eadf8

File tree

7 files changed

+138
-68
lines changed

7 files changed

+138
-68
lines changed

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.PHONY: proto
2+
proto: install
3+
protoc --go-frpc_out=./pkg/generator ./examples/test/test.proto
4+
5+
.PHONY: install
6+
install:
7+
go install ./protoc-gen-go-frpc
8+
9+
.PHONY: test
10+
test: proto
11+
go test -v ./...

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ module github.com/loopholelabs/frpc-go
33
go 1.22
44

55
require (
6-
github.com/loopholelabs/frisbee-go v0.10.0
6+
github.com/loopholelabs/frisbee-go v0.10.2
77
github.com/loopholelabs/logging v0.3.1
8-
github.com/loopholelabs/polyglot/v2 v2.0.2
8+
github.com/loopholelabs/polyglot/v2 v2.0.3
99
github.com/loopholelabs/testing v0.2.3
1010
github.com/stretchr/testify v1.9.0
1111
google.golang.org/protobuf v1.35.1
1212
)
1313

1414
require (
1515
github.com/davecgh/go-spew v1.1.1 // indirect
16-
github.com/loopholelabs/common v0.4.9 // indirect
16+
github.com/loopholelabs/common v0.4.10 // indirect
1717
github.com/pmezard/go-difflib v1.0.0 // indirect
1818
gopkg.in/yaml.v3 v3.0.1 // indirect
1919
)

go.sum

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
22
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
33
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
44
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
5-
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
6-
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
5+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
6+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
77
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
88
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
9-
github.com/loopholelabs/common v0.4.9 h1:9MPUYlZZ/qx3Kt8LXgXxcSXthrM91od8026c4DlGpAU=
10-
github.com/loopholelabs/common v0.4.9/go.mod h1:Wop5srN1wYT+mdQ9gZ+kn2I9qKAyVd0FB48pThwIa9M=
11-
github.com/loopholelabs/frisbee-go v0.10.0 h1:eqdDqm44V23GMxhjDL9OBz2Fxecsu42M0KHM8kQObBQ=
12-
github.com/loopholelabs/frisbee-go v0.10.0/go.mod h1:RwKglItbNcQq9UW6Vm2aOwOWXR6wTxUNjvuXcCIyJkU=
9+
github.com/loopholelabs/common v0.4.10 h1:BMJSMwH0PiVtdpOlXNPlW827B9WPJ/Gkb/q20NLeOjw=
10+
github.com/loopholelabs/common v0.4.10/go.mod h1:wc17hLpzZaDbndb7Fh3MXQDnhf4Cmf/JKC+LmXaD6II=
11+
github.com/loopholelabs/frisbee-go v0.10.2 h1:TvfONSpoCrrvG7/8QzBbHou2gPqOwN5iFL+4fp3bu4s=
12+
github.com/loopholelabs/frisbee-go v0.10.2/go.mod h1:JJhInJ5zjxpwOLAqEviOjTo1k8E3NNEF/fQTrC4lZ/4=
1313
github.com/loopholelabs/logging v0.3.1 h1:VA9DF3WrbmvJC1uQJ/XcWgz8KWXydWwe3BdDiMbN2FY=
1414
github.com/loopholelabs/logging v0.3.1/go.mod h1:uRDUydiqPqKbZkb0WoQ3dfyAcJ2iOMhxdEafZssLVv0=
15-
github.com/loopholelabs/polyglot/v2 v2.0.2 h1:v308fg2ZKSvkKDnWgBnDvvmiu4YypCxcDe5Ih5GUVnY=
16-
github.com/loopholelabs/polyglot/v2 v2.0.2/go.mod h1:kFoSKvnKAWmV0ICfbaCHDv/+cz5LSuA+xXG4WtYV/z4=
15+
github.com/loopholelabs/polyglot/v2 v2.0.3 h1:CpH2az5shkOgOBASnzjc1XC5SVzQMbWyHt4R7ds/FFc=
16+
github.com/loopholelabs/polyglot/v2 v2.0.3/go.mod h1:yodgE9ile0RS/npD0WnHfFpMLvL5FlC9n3qZ1tTkB9g=
1717
github.com/loopholelabs/testing v0.2.3 h1:4nVuK5ctaE6ua5Z0dYk2l7xTFmcpCYLUeGjRBp8keOA=
1818
github.com/loopholelabs/testing v0.2.3/go.mod h1:gqtGY91soYD1fQoKQt/6kP14OYpS7gcbcIgq5mc9m8Q=
1919
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@@ -22,6 +22,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
2222
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
2323
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2424
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
25+
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
26+
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
2527
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
2628
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
2729
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
@@ -35,7 +37,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
3537
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
3638
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
3739
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
38-
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
39-
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
40+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
41+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
4042
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
4143
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

pkg/generator/test/generator_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,18 @@ package test
44

55
import (
66
"context"
7+
"fmt"
78
"io"
9+
"net/http"
10+
"net/http/httptest"
11+
"os"
812
"testing"
13+
"time"
914

15+
"github.com/loopholelabs/polyglot/v2"
1016
"github.com/loopholelabs/testing/conn/pair"
1117
"github.com/stretchr/testify/assert"
18+
"github.com/stretchr/testify/require"
1219
)
1320

1421
func TestRPC(t *testing.T) {
@@ -130,3 +137,51 @@ func testClientStreaming(client *Client, t *testing.T) {
130137
assert.NoError(t, err)
131138
assert.Equal(t, "Hello World", res.Message)
132139
}
140+
141+
func TestRPCInvalidConnection(t *testing.T) {
142+
// Create non-Frisbee server the client can connect to but not exchange
143+
// messages, so the connection will be broken soon after connect.
144+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
145+
fmt.Fprintln(w, "test")
146+
}))
147+
t.Cleanup(ts.Close)
148+
149+
// Create a client and connect to test server.
150+
client, err := NewClient(nil, nil)
151+
require.NoError(t, err)
152+
153+
err = client.Connect(ts.Listener.Addr().String())
154+
require.NoError(t, err)
155+
156+
// Make RPC request with a 3s timeout.
157+
timeout := 3 * time.Second
158+
if os.Getenv("CI") != "" {
159+
timeout = 10 * time.Second
160+
}
161+
ctx, cancel := context.WithTimeout(context.Background(), timeout)
162+
t.Cleanup(cancel)
163+
164+
req := &Request{
165+
Message: "Hello World",
166+
Corpus: RequestUNIVERSAL,
167+
}
168+
response, err := client.EchoService.Echo(ctx, req)
169+
170+
// Verify request doesn't block forever.
171+
require.NoError(t, ctx.Err())
172+
173+
// Verify request fails.
174+
require.Error(t, err)
175+
require.Nil(t, response)
176+
}
177+
178+
func TestEncodeDecodePreservesNilFields(t *testing.T) {
179+
r := &Response{Message: "test", Test: nil}
180+
b := polyglot.NewBuffer()
181+
r.Encode(b)
182+
183+
got := &Response{}
184+
err := got.Decode(b.Bytes())
185+
require.NoError(t, err)
186+
require.Equal(t, r, got)
187+
}

pkg/generator/test/test.frpc.go

Lines changed: 51 additions & 55 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)