starpc 0.48.0 → 0.49.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  //go:build deps_only
2
2
 
3
3
  // Code generated by protoc-gen-starpc-cpp. DO NOT EDIT.
4
- // protoc-gen-starpc-cpp version: v0.45.1-0.20260203090429-3e915608d4e8
4
+ // protoc-gen-starpc-cpp version: v0.46.1
5
5
  // source: github.com/aperturerobotics/starpc/echo/echo.proto
6
6
 
7
7
  #include "echo_srpc.pb.hpp"
@@ -1,5 +1,5 @@
1
1
  // Code generated by protoc-gen-srpc. DO NOT EDIT.
2
- // protoc-gen-srpc version: v0.45.1-0.20260203090429-3e915608d4e8
2
+ // protoc-gen-srpc version: v0.46.1
3
3
  // source: github.com/aperturerobotics/starpc/echo/echo.proto
4
4
 
5
5
  package echo
@@ -1,7 +1,7 @@
1
1
  //go:build deps_only && cgo
2
2
 
3
3
  // Code generated by protoc-gen-starpc-cpp. DO NOT EDIT.
4
- // protoc-gen-starpc-cpp version: v0.45.1-0.20260203090429-3e915608d4e8
4
+ // protoc-gen-starpc-cpp version: v0.46.1
5
5
  // source: github.com/aperturerobotics/starpc/echo/echo.proto
6
6
 
7
7
  #pragma once
@@ -1,5 +1,5 @@
1
1
  // Code generated by protoc-gen-starpc-rust. DO NOT EDIT.
2
- // protoc-gen-starpc-rust version: v0.45.1-0.20260203090429-3e915608d4e8
2
+ // protoc-gen-starpc-rust version: v0.46.1
3
3
  // source: github.com/aperturerobotics/starpc/echo/echo.proto
4
4
 
5
5
  #[allow(unused_imports)]
package/go.mod CHANGED
@@ -3,32 +3,32 @@ module github.com/aperturerobotics/starpc
3
3
  go 1.25
4
4
 
5
5
  require (
6
- github.com/aperturerobotics/common v0.30.7 // latest
6
+ github.com/aperturerobotics/common v0.32.0 // latest
7
7
  github.com/aperturerobotics/protobuf-go-lite v0.12.2 // latest
8
- github.com/aperturerobotics/util v1.32.4 // latest
8
+ github.com/aperturerobotics/util v1.32.5 // latest
9
9
  )
10
10
 
11
11
  require (
12
12
  github.com/aperturerobotics/abseil-cpp v0.0.0-20260131110040-4bb56e2f9017 // aperture-2
13
13
  github.com/aperturerobotics/cli v1.1.0 // indirect
14
- github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260131050911-b5f94b044584 // indirect
14
+ github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260219012250-c573f70e4509 // indirect
15
15
  github.com/aperturerobotics/json-iterator-lite v1.0.1-0.20251104042408-0c9eb8a3f726 // indirect
16
16
  github.com/aperturerobotics/protobuf v0.0.0-20260203024654-8201686529c4 // wasi
17
17
  )
18
18
 
19
19
  require (
20
- github.com/aperturerobotics/go-websocket v1.8.15-0.20260228104546-35e37959349c // master
20
+ github.com/aperturerobotics/go-websocket v1.8.15-0.20260228132212-c5f88237fdf8 // master
21
21
  github.com/libp2p/go-yamux/v4 v4.0.2 // latest
22
22
  github.com/pkg/errors v0.9.1 // latest
23
- github.com/sirupsen/logrus v1.9.5-0.20260226151524-34027eac4204 // latest
23
+ github.com/sirupsen/logrus v1.9.5-0.20260309202648-9f0600962f75 // latest
24
24
  google.golang.org/protobuf v1.36.11 // latest
25
25
  )
26
26
 
27
27
  require (
28
- github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260204215916-dc1f0fed8cfc // indirect
28
+ github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260219012250-e75aa9f430a2 // indirect
29
29
  github.com/libp2p/go-buffer-pool v0.1.0 // indirect
30
30
  github.com/tetratelabs/wazero v1.11.0 // indirect
31
31
  github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect
32
32
  golang.org/x/mod v0.33.0 // indirect
33
- golang.org/x/sys v0.40.0 // indirect
33
+ golang.org/x/sys v0.41.0 // indirect
34
34
  )
package/go.sum CHANGED
@@ -2,22 +2,22 @@ github.com/aperturerobotics/abseil-cpp v0.0.0-20260131110040-4bb56e2f9017 h1:3U7
2
2
  github.com/aperturerobotics/abseil-cpp v0.0.0-20260131110040-4bb56e2f9017/go.mod h1:lNSJTKECIUFAnfeSqy01kXYTYe1BHubW7198jNX3nEw=
3
3
  github.com/aperturerobotics/cli v1.1.0 h1:7a+YRC+EY3npAnTzhHV5gLCiw91KS0Ts3XwLILGOsT8=
4
4
  github.com/aperturerobotics/cli v1.1.0/go.mod h1:M7BFP9wow5ytTzMyJQOOO991fGfsUqdTI7gGEsHfTQ8=
5
- github.com/aperturerobotics/common v0.30.7 h1:MfbgBHwRnCFPCWVXjKiqEBkFGpMf8Nk2pRsUiSuzxSc=
6
- github.com/aperturerobotics/common v0.30.7/go.mod h1:rdhYmixjslfBT0lEMADQHWjgRKIDxZ+8Paxv4lGZfp0=
7
- github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260204215916-dc1f0fed8cfc h1:MQKYvrnue6iT6DlQdGbaBYvyY/WuR63cbhUtRZjx4OM=
8
- github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260204215916-dc1f0fed8cfc/go.mod h1:OBb/beWmr/pDIZAUfi86j/4tBh2v5ctTxKMqSnh9c/4=
9
- github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260131050911-b5f94b044584 h1:ER8DYYL71cTg39uZ+Gi699tL/hZoscUWDOw4DbizqhI=
10
- github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260131050911-b5f94b044584/go.mod h1:vEq8i7EKb32+KXGtIEZjjhNns+BdsL2dUMw4uhy3578=
11
- github.com/aperturerobotics/go-websocket v1.8.15-0.20260228104546-35e37959349c h1:JATtwVM+rTNr0cwQhr9GtDRdi0EsLmfQ0dIT8SlhGMk=
12
- github.com/aperturerobotics/go-websocket v1.8.15-0.20260228104546-35e37959349c/go.mod h1:9KnSGuqxSXbdB/Oi0I6vvfPLkclfJwMGAQaDDGVgGow=
5
+ github.com/aperturerobotics/common v0.32.0 h1:wKKKUMGs8HZC2exPGVNtj81zqW687mf/rhSbme45CFs=
6
+ github.com/aperturerobotics/common v0.32.0/go.mod h1:m46OxaMDdhPjsWBSdIwkPPCmuFVMuJiRc8/DvC0wStM=
7
+ github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260219012250-e75aa9f430a2 h1:DbLHhYu2wdFvwQ02KE3rxBE1vSc+ecMxLEyTwv8Ci00=
8
+ github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260219012250-e75aa9f430a2/go.mod h1:OBb/beWmr/pDIZAUfi86j/4tBh2v5ctTxKMqSnh9c/4=
9
+ github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260219012250-c573f70e4509 h1:2O8djle14ujtze1xQb9MVXAdEepumxhoBJPEdf+kNnA=
10
+ github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260219012250-c573f70e4509/go.mod h1:vEq8i7EKb32+KXGtIEZjjhNns+BdsL2dUMw4uhy3578=
11
+ github.com/aperturerobotics/go-websocket v1.8.15-0.20260228132212-c5f88237fdf8 h1:OWYqCKoDoPGXgkD1y465qb8L5odv3nGabeN9wF50GxU=
12
+ github.com/aperturerobotics/go-websocket v1.8.15-0.20260228132212-c5f88237fdf8/go.mod h1:9KnSGuqxSXbdB/Oi0I6vvfPLkclfJwMGAQaDDGVgGow=
13
13
  github.com/aperturerobotics/json-iterator-lite v1.0.1-0.20251104042408-0c9eb8a3f726 h1:4B1F0DzuqPzb6WqgCjWaqDD7JU9RDsevQG5OP0DFBgs=
14
14
  github.com/aperturerobotics/json-iterator-lite v1.0.1-0.20251104042408-0c9eb8a3f726/go.mod h1:SvGGBv3OVxUyqO0ZxA/nvs6z3cg7NIbZ64TnbV2OISo=
15
15
  github.com/aperturerobotics/protobuf v0.0.0-20260203024654-8201686529c4 h1:4Dy3BAHh2kgVdHAqtlwcFsgY0kAwUe2m3rfFcaGwGQg=
16
16
  github.com/aperturerobotics/protobuf v0.0.0-20260203024654-8201686529c4/go.mod h1:tMgO7y6SJo/d9ZcvrpNqIQtdYT9de+QmYaHOZ4KnhOg=
17
17
  github.com/aperturerobotics/protobuf-go-lite v0.12.2 h1:ujwTKgpIYAVsv8VljB6PPMY5SI8i8m/NlIHAkkuxQcU=
18
18
  github.com/aperturerobotics/protobuf-go-lite v0.12.2/go.mod h1:lGH3s5ArCTXKI4wJdlNpaybUtwSjfAG0vdWjxOfMcF8=
19
- github.com/aperturerobotics/util v1.32.4 h1:+f4rZuGC0AfgvA6bEwxSiW3Px9awyqr/CB2ltHyX4a8=
20
- github.com/aperturerobotics/util v1.32.4/go.mod h1:qfRZZUDn0sEfc43JRA6rKP8bwFxliqGqJZX+p1jeOnQ=
19
+ github.com/aperturerobotics/util v1.32.5 h1:5gr2Ft1rYGFA5yrSm3n18Ghu5Al2uyg7DTtljh1wLHI=
20
+ github.com/aperturerobotics/util v1.32.5/go.mod h1:jXVTz1OQqM3VSrjy06qiCmJLaUm80NxVuKgweaRs5dg=
21
21
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
22
22
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
23
23
  github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -30,8 +30,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
30
30
  github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
31
31
  github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
32
32
  github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
33
- github.com/sirupsen/logrus v1.9.5-0.20260226151524-34027eac4204 h1:YnveTg6h00DNa0rUaIImUk3icr3BIMedAbJparp1bVM=
34
- github.com/sirupsen/logrus v1.9.5-0.20260226151524-34027eac4204/go.mod h1:FXZFonkDAnFozmO+5hGAFvB0Yg9/j2SIhA/QuIkP180=
33
+ github.com/sirupsen/logrus v1.9.5-0.20260309202648-9f0600962f75 h1:JeSpp4dH/b6DwynjIVL8YDlOSNrEtfhhPajrT3CUUTI=
34
+ github.com/sirupsen/logrus v1.9.5-0.20260309202648-9f0600962f75/go.mod h1:FXZFonkDAnFozmO+5hGAFvB0Yg9/j2SIhA/QuIkP180=
35
35
  github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
36
36
  github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
37
37
  github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
@@ -40,8 +40,8 @@ github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAz
40
40
  github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
41
41
  golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
42
42
  golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
43
- golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
44
- golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
43
+ golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
44
+ golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
45
45
  google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
46
46
  google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
47
47
  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
@@ -1,7 +1,7 @@
1
1
  //go:build deps_only
2
2
 
3
3
  // Code generated by protoc-gen-starpc-cpp. DO NOT EDIT.
4
- // protoc-gen-starpc-cpp version: v0.45.1-0.20260203090429-3e915608d4e8
4
+ // protoc-gen-starpc-cpp version: v0.46.1
5
5
  // source: github.com/aperturerobotics/starpc/mock/mock.proto
6
6
 
7
7
  #include "mock_srpc.pb.hpp"
@@ -1,5 +1,5 @@
1
1
  // Code generated by protoc-gen-srpc. DO NOT EDIT.
2
- // protoc-gen-srpc version: v0.45.1-0.20260203090429-3e915608d4e8
2
+ // protoc-gen-srpc version: v0.46.1
3
3
  // source: github.com/aperturerobotics/starpc/mock/mock.proto
4
4
 
5
5
  package e2e_mock
@@ -1,7 +1,7 @@
1
1
  //go:build deps_only && cgo
2
2
 
3
3
  // Code generated by protoc-gen-starpc-cpp. DO NOT EDIT.
4
- // protoc-gen-starpc-cpp version: v0.45.1-0.20260203090429-3e915608d4e8
4
+ // protoc-gen-starpc-cpp version: v0.46.1
5
5
  // source: github.com/aperturerobotics/starpc/mock/mock.proto
6
6
 
7
7
  #pragma once
@@ -1,5 +1,5 @@
1
1
  // Code generated by protoc-gen-starpc-rust. DO NOT EDIT.
2
- // protoc-gen-starpc-rust version: v0.45.1-0.20260203090429-3e915608d4e8
2
+ // protoc-gen-starpc-rust version: v0.46.1
3
3
  // source: github.com/aperturerobotics/starpc/mock/mock.proto
4
4
 
5
5
  #[allow(unused_imports)]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.48.0",
3
+ "version": "0.49.1",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -96,9 +96,10 @@
96
96
  "precommit": "lint-staged",
97
97
  "release": "npm run release:version && npm run release:commit",
98
98
  "release:minor": "npm run release:version:minor && npm run release:commit",
99
- "release:version": "npm version patch -m \"release: v%s\" --no-git-tag-version",
100
- "release:version:minor": "npm version minor -m \"release: v%s\" --no-git-tag-version",
101
- "release:commit": "git reset && git add package.json && git commit -s -m \"release: v$(node -p \"require('./package.json').version\")\" && git tag v$(node -p \"require('./package.json').version\")",
99
+ "release:version": "npm version patch -m \"release: v%s\" --no-git-tag-version && npm run release:sync-cargo",
100
+ "release:version:minor": "npm version minor -m \"release: v%s\" --no-git-tag-version && npm run release:sync-cargo",
101
+ "release:sync-cargo": "node -e \"const v=require('./package.json').version,f='Cargo.toml',c=require('fs').readFileSync(f,'utf8');require('fs').writeFileSync(f,c.replace(/^version = \\\".*?\\\"/m,'version = \\\"'+v+'\\\"'))\"",
102
+ "release:commit": "git reset && git add package.json Cargo.toml && git commit -s -m \"release: v$(node -p \"require('./package.json').version\")\" && git tag v$(node -p \"require('./package.json').version\")",
102
103
  "release:publish": "git push && git push --tags"
103
104
  },
104
105
  "preferUnplugged": true,
@@ -141,6 +142,6 @@
141
142
  "ws": "^8.18.1"
142
143
  },
143
144
  "resolutions": {
144
- "@aptre/protobuf-es-lite": "1.0.1"
145
+ "@aptre/protobuf-es-lite": "1.0.2"
145
146
  }
146
147
  }
package/srpc/rwc-conn.go CHANGED
@@ -12,27 +12,56 @@ import (
12
12
  // connPktSize is the size of the buffers to use for packets for the RwcConn.
13
13
  const connPktSize = 2048
14
14
 
15
- // RwcConn implements a Conn with a buffered ReadWriteCloser.
15
+ // bufPool is a channel-based buffer pool with predictable reuse behavior.
16
+ type bufPool struct {
17
+ ch chan []byte
18
+ size int
19
+ }
20
+
21
+ func newBufPool(poolSize, bufSize int) *bufPool {
22
+ return &bufPool{
23
+ ch: make(chan []byte, poolSize),
24
+ size: bufSize,
25
+ }
26
+ }
27
+
28
+ func (p *bufPool) get() []byte {
29
+ select {
30
+ case b := <-p.ch:
31
+ return b[:p.size]
32
+ default:
33
+ return make([]byte, p.size)
34
+ }
35
+ }
36
+
37
+ func (p *bufPool) put(b []byte) {
38
+ select {
39
+ case p.ch <- b:
40
+ default:
41
+ }
42
+ }
43
+
44
+ // RwcConn implements a net.Conn with a buffered ReadWriteCloser.
16
45
  type RwcConn struct {
17
- // ctx is the context
18
- ctx context.Context
19
- // ctxCancel is the context canceler
46
+ ctx context.Context
20
47
  ctxCancel context.CancelFunc
21
- // rwc is the read-write-closer
22
- rwc io.ReadWriteCloser
23
- // laddr is the local addr
24
- laddr net.Addr
25
- // raddr is the remote addr
26
- raddr net.Addr
27
-
28
- ar sync.Pool // packet arena
29
- rd time.Time // read deadline
30
- wd time.Time // write deadline
31
- packetCh chan []byte // packet ch
48
+ rwc io.ReadWriteCloser
49
+ laddr net.Addr
50
+ raddr net.Addr
51
+
52
+ pool *bufPool
53
+ packetCh chan []byte
54
+
55
+ mu sync.Mutex
56
+ rd time.Time
57
+ wd time.Time
32
58
  closeErr error
59
+
60
+ pendingMu sync.Mutex
61
+ pending []byte
33
62
  }
34
63
 
35
- // NewRwcConn constructs a new packet conn and starts the rx pump.
64
+ // NewRwcConn constructs a new RwcConn and starts the rx pump.
36
65
  func NewRwcConn(
37
66
  ctx context.Context,
38
67
  rwc io.ReadWriteCloser,
@@ -50,11 +79,10 @@ func NewRwcConn(
50
79
  rwc: rwc,
51
80
  laddr: laddr,
52
81
  raddr: raddr,
82
+ pool: newBufPool(bufferPacketN, connPktSize),
53
83
  packetCh: make(chan []byte, bufferPacketN),
54
84
  }
55
- go func() {
56
- _ = c.rxPump()
57
- }()
85
+ go c.rxPump()
58
86
  return c
59
87
  }
60
88
 
@@ -68,54 +96,91 @@ func (p *RwcConn) RemoteAddr() net.Addr {
68
96
  return p.raddr
69
97
  }
70
98
 
99
+ // readDeadline returns the current read deadline under the mutex.
100
+ func (p *RwcConn) readDeadline() time.Time {
101
+ p.mu.Lock()
102
+ defer p.mu.Unlock()
103
+ return p.rd
104
+ }
105
+
106
+ // getCloseErr returns the close error under the mutex.
107
+ func (p *RwcConn) getCloseErr() error {
108
+ p.mu.Lock()
109
+ defer p.mu.Unlock()
110
+ return p.closeErr
111
+ }
112
+
113
+ // setCloseErr stores the close error under the mutex.
114
+ func (p *RwcConn) setCloseErr(err error) {
115
+ p.mu.Lock()
116
+ defer p.mu.Unlock()
117
+ p.closeErr = err
118
+ }
119
+
71
120
  // Read reads data from the connection.
72
121
  // Read can be made to time out and return an error after a fixed
73
122
  // time limit; see SetDeadline and SetReadDeadline.
74
- func (p *RwcConn) Read(b []byte) (n int, err error) {
75
- deadline := p.rd
123
+ func (p *RwcConn) Read(b []byte) (int, error) {
124
+ // Drain pending data from a previous partial read first.
125
+ p.pendingMu.Lock()
126
+ if len(p.pending) > 0 {
127
+ n := copy(b, p.pending)
128
+ if n < len(p.pending) {
129
+ p.pending = p.pending[n:]
130
+ } else {
131
+ p.pending = nil
132
+ }
133
+ p.pendingMu.Unlock()
134
+ return n, nil
135
+ }
136
+ p.pendingMu.Unlock()
137
+
138
+ // Build a context with the read deadline if one is set.
76
139
  ctx := p.ctx
140
+ deadline := p.readDeadline()
77
141
  if !deadline.IsZero() {
78
- var ctxCancel context.CancelFunc
79
- ctx, ctxCancel = context.WithDeadline(ctx, deadline)
80
- defer ctxCancel()
142
+ var cancel context.CancelFunc
143
+ ctx, cancel = context.WithDeadline(ctx, deadline)
144
+ defer cancel()
81
145
  }
82
146
 
83
- var pkt []byte
84
- var ok bool
85
147
  select {
86
148
  case <-ctx.Done():
87
149
  if !deadline.IsZero() {
88
150
  return 0, os.ErrDeadlineExceeded
89
151
  }
90
152
  return 0, context.Canceled
91
- case pkt, ok = <-p.packetCh:
153
+ case pkt, ok := <-p.packetCh:
92
154
  if !ok {
93
- err = p.closeErr
155
+ err := p.getCloseErr()
94
156
  if err == nil {
95
157
  err = io.EOF
96
158
  }
97
159
  return 0, err
98
160
  }
99
- }
100
161
 
101
- pl := len(pkt)
102
- copy(b, pkt)
103
- p.ar.Put(&pkt)
104
- if len(b) < pl {
105
- return len(b), io.ErrShortBuffer
162
+ n := copy(b, pkt)
163
+ if n < len(pkt) {
164
+ // Buffer the remaining bytes for the next Read call.
165
+ // Explicitly copy so the pool buffer is not aliased.
166
+ p.pendingMu.Lock()
167
+ p.pending = append(p.pending[:0], pkt[n:]...)
168
+ p.pendingMu.Unlock()
169
+ }
170
+ p.pool.put(pkt)
171
+ return n, nil
106
172
  }
107
- return pl, nil
108
173
  }
109
174
 
110
175
  // Write writes data to the connection.
111
- func (p *RwcConn) Write(pkt []byte) (n int, err error) {
176
+ func (p *RwcConn) Write(pkt []byte) (int, error) {
112
177
  if len(pkt) == 0 {
113
178
  return 0, nil
114
179
  }
115
180
 
116
181
  written := 0
117
182
  for written < len(pkt) {
118
- n, err = p.rwc.Write(pkt[written:])
183
+ n, err := p.rwc.Write(pkt[written:])
119
184
  written += n
120
185
  if err != nil {
121
186
  return written, err
@@ -124,106 +189,74 @@ func (p *RwcConn) Write(pkt []byte) (n int, err error) {
124
189
  return written, nil
125
190
  }
126
191
 
127
- // SetDeadline sets the read and write deadlines associated
128
- // with the connection. It is equivalent to calling both
129
- // SetReadDeadline and SetWriteDeadline.
130
- //
131
- // A deadline is an absolute time after which I/O operations
132
- // fail instead of blocking. The deadline applies to all future
133
- // and pending I/O, not just the immediately following call to
134
- // Read or Write. After a deadline has been exceeded, the
135
- // connection can be refreshed by setting a deadline in the future.
136
- //
137
- // If the deadline is exceeded a call to Read or Write or to other
138
- // I/O methods will return an error that wraps os.ErrDeadlineExceeded.
139
- // This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
140
- // The error's Timeout method will return true, but note that there
141
- // are other possible errors for which the Timeout method will
142
- // return true even if the deadline has not been exceeded.
143
- //
144
- // An idle timeout can be implemented by repeatedly extending
145
- // the deadline after successful ReadFrom or WriteTo calls.
192
+ // SetDeadline sets the read and write deadlines associated with the
193
+ // connection. It is equivalent to calling both SetReadDeadline and
194
+ // SetWriteDeadline.
146
195
  //
147
196
  // A zero value for t means I/O operations will not time out.
148
197
  func (p *RwcConn) SetDeadline(t time.Time) error {
198
+ p.mu.Lock()
199
+ defer p.mu.Unlock()
149
200
  p.rd = t
150
201
  p.wd = t
151
202
  return nil
152
203
  }
153
204
 
154
- // SetReadDeadline sets the deadline for future ReadFrom calls
155
- // and any currently-blocked ReadFrom call.
156
- // A zero value for t means ReadFrom will not time out.
205
+ // SetReadDeadline sets the deadline for future Read calls and any
206
+ // currently-blocked Read call.
207
+ // A zero value for t means Read will not time out.
157
208
  func (p *RwcConn) SetReadDeadline(t time.Time) error {
209
+ p.mu.Lock()
210
+ defer p.mu.Unlock()
158
211
  p.rd = t
159
212
  return nil
160
213
  }
161
214
 
162
- // SetWriteDeadline sets the deadline for future WriteTo calls
163
- // and any currently-blocked WriteTo call.
164
- // Even if write times out, it may return n > 0, indicating that
165
- // some of the data was successfully written.
166
- // A zero value for t means WriteTo will not time out.
215
+ // SetWriteDeadline sets the deadline for future Write calls and any
216
+ // currently-blocked Write call.
217
+ // A zero value for t means Write will not time out.
167
218
  func (p *RwcConn) SetWriteDeadline(t time.Time) error {
219
+ p.mu.Lock()
220
+ defer p.mu.Unlock()
168
221
  p.wd = t
169
222
  return nil
170
223
  }
171
224
 
172
225
  // Close closes the connection.
173
- // Any blocked ReadFrom or WriteTo operations will be unblocked and return errors.
226
+ // Any blocked Read or Write operations will be unblocked and return errors.
174
227
  func (p *RwcConn) Close() error {
228
+ p.ctxCancel()
175
229
  return p.rwc.Close()
176
230
  }
177
231
 
178
- // getArenaBuf returns a buf from the packet arena with at least the given size
179
- func (p *RwcConn) getArenaBuf(size int) []byte {
180
- var buf []byte
181
- bufp := p.ar.Get()
182
- if bufp != nil {
183
- buf = *bufp.(*[]byte)
184
- }
185
- if size != 0 {
186
- if cap(buf) < size {
187
- buf = make([]byte, size)
188
- } else {
189
- buf = buf[:size]
190
- }
191
- } else {
192
- buf = buf[:cap(buf)]
193
- }
194
- return buf
195
- }
196
-
197
232
  // rxPump receives messages from the underlying connection.
198
- func (p *RwcConn) rxPump() (rerr error) {
233
+ func (p *RwcConn) rxPump() {
234
+ var rerr error
199
235
  defer func() {
200
- p.closeErr = rerr
236
+ p.setCloseErr(rerr)
201
237
  close(p.packetCh)
202
238
  }()
203
239
 
204
240
  for {
205
- select {
206
- case <-p.ctx.Done():
207
- return p.ctx.Err()
208
- default:
209
- }
210
-
211
- pktBuf := p.getArenaBuf(int(connPktSize))
212
- n, err := p.rwc.Read(pktBuf)
241
+ buf := p.pool.get()
242
+ n, err := p.rwc.Read(buf)
213
243
  if n == 0 {
214
- p.ar.Put(&pktBuf)
244
+ p.pool.put(buf)
215
245
  } else {
216
246
  select {
217
247
  case <-p.ctx.Done():
218
- return context.Canceled
219
- case p.packetCh <- pktBuf[:n]:
248
+ p.pool.put(buf)
249
+ rerr = context.Canceled
250
+ return
251
+ case p.packetCh <- buf[:n]:
220
252
  }
221
253
  }
222
254
  if err != nil {
223
- return err
255
+ rerr = err
256
+ return
224
257
  }
225
258
  }
226
259
  }
227
260
 
228
261
  // _ is a type assertion
229
- var _ net.Conn = ((*RwcConn)(nil))
262
+ var _ net.Conn = (*RwcConn)(nil)
@@ -0,0 +1,51 @@
1
+ package srpc
2
+
3
+ import (
4
+ "bytes"
5
+ "context"
6
+ "io"
7
+ "testing"
8
+ )
9
+
10
+ // blockingRWC wraps a pipe for testing RwcConn.
11
+ type blockingRWC struct {
12
+ r io.Reader
13
+ w io.Writer
14
+ }
15
+
16
+ func (b *blockingRWC) Read(p []byte) (int, error) { return b.r.Read(p) }
17
+ func (b *blockingRWC) Write(p []byte) (int, error) { return b.w.Write(p) }
18
+ func (b *blockingRWC) Close() error { return nil }
19
+
20
+ // TestRwcConnReadBuffering verifies that RwcConn.Read does not drop data
21
+ // when the caller's buffer is smaller than the received packet.
22
+ func TestRwcConnReadBuffering(t *testing.T) {
23
+ pr, pw := io.Pipe()
24
+ rwc := &blockingRWC{r: pr, w: pw}
25
+
26
+ ctx, cancel := context.WithCancel(t.Context())
27
+ defer cancel()
28
+
29
+ conn := NewRwcConn(ctx, rwc, nil, nil, 10)
30
+
31
+ // Write a 100-byte message through the pipe.
32
+ msg := bytes.Repeat([]byte("abcdefghij"), 10) // 100 bytes
33
+ go func() {
34
+ _, _ = pw.Write(msg)
35
+ }()
36
+
37
+ // Read in small chunks (16 bytes at a time) to simulate bufio.Reader.
38
+ var got []byte
39
+ buf := make([]byte, 16)
40
+ for len(got) < len(msg) {
41
+ n, err := conn.Read(buf)
42
+ if err != nil {
43
+ t.Fatalf("Read error after %d bytes: %v", len(got), err)
44
+ }
45
+ got = append(got, buf[:n]...)
46
+ }
47
+
48
+ if !bytes.Equal(got, msg) {
49
+ t.Fatalf("data mismatch: got %d bytes, want %d bytes\ngot: %q\nwant: %q", len(got), len(msg), got, msg)
50
+ }
51
+ }
package/srpc/Cargo.toml DELETED
@@ -1,25 +0,0 @@
1
- [package]
2
- name = "starpc"
3
- version.workspace = true
4
- edition.workspace = true
5
- rust-version.workspace = true
6
- license.workspace = true
7
- repository.workspace = true
8
- description.workspace = true
9
- keywords.workspace = true
10
- categories.workspace = true
11
-
12
- [lib]
13
- path = "lib.rs"
14
-
15
- [dependencies]
16
- prost = { workspace = true }
17
- bytes = { workspace = true }
18
- tokio = { workspace = true }
19
- tokio-util = { workspace = true }
20
- futures = { workspace = true }
21
- thiserror = { workspace = true }
22
- async-trait = { workspace = true }
23
- pin-project-lite = { workspace = true }
24
- scopeguard = { workspace = true }
25
-