-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This adds a new message type for passing file descriptors. How this works is: 1. Client sends a message with a header for messageTypeFileDescriptor along with the list of descriptors to be sent 2. Client sends 2nd message to actually pass along the descriptors (needed for unix sockets). 3. Server sees the message type and waits to receive the fd's. 4. Once fd's are seen the server responds with the real fd numbers that are used which an application can use in future calls. To accomplish this reliably (on unix sockets) I had to drop the usage of the bufio.Reader because we need to ensure exact message boundaries. Within ttrpc this only support unix sockets and `net.Conn` implementations that implement `SendFds`/`ReceiveFds` (this interface is totally invented here). Something to consider, I have not attempted to do fd passing on Windows which will need other mechanisms entirely (and the conn's provided by winio are not sufficient for fd passing). I'm not sure if this new messaging will actually work on a Windows implementation. Perhaps the message tpye should be specifically for unix sockets? I'm not sure how this would be enforced at the moment except by checking if the `net.Conn` is a `*net.UnixConn`. Signed-off-by: Brian Goff <[email protected]>
- Loading branch information
Showing
6 changed files
with
366 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package ttrpc | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"io" | ||
"net" | ||
"os" | ||
"strconv" | ||
"testing" | ||
"time" | ||
) | ||
|
||
type HandshakerFunc func(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) | ||
|
||
func (f HandshakerFunc) Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) { | ||
return f(ctx, conn) | ||
} | ||
|
||
func TestSendRecevFd(t *testing.T) { | ||
var ( | ||
ctx, cancel = context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) | ||
server = mustServer(t)(NewServer()) | ||
testImpl = &testingServerFd{respData: []byte("hello")} | ||
addr, listener = newTestListener(t) | ||
) | ||
|
||
defer cancel() | ||
defer listener.Close() | ||
|
||
server.Register("Test", map[string]Method{ | ||
"Test": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { | ||
req := &testFdPayload{} | ||
|
||
if err := unmarshal(req); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &testFdPayload{}, testImpl.Test(ctx, req) | ||
}, | ||
}) | ||
|
||
go server.Serve(ctx, listener) | ||
defer server.Shutdown(ctx) | ||
|
||
var ( | ||
client, cleanup = newTestClient(t, addr) | ||
|
||
tclient = testFdClient{client} | ||
) | ||
defer cleanup() | ||
|
||
r, w, err := os.Pipe() | ||
if err != nil { | ||
t.Fatal(err, "error creating test pipe") | ||
} | ||
defer r.Close() | ||
|
||
type readResp struct { | ||
buf []byte | ||
err error | ||
} | ||
|
||
chResp := make(chan readResp, 1) | ||
go func() { | ||
buf := make([]byte, len(testImpl.respData)) | ||
_, err := io.ReadFull(r, buf) | ||
chResp <- readResp{buf, err} | ||
}() | ||
|
||
if err := tclient.Test(ctx, w); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
select { | ||
case <-ctx.Done(): | ||
t.Fatal(ctx.Err()) | ||
case resp := <-chResp: | ||
if resp.err != nil { | ||
t.Error(err) | ||
} | ||
if !bytes.Equal(resp.buf, testImpl.respData) { | ||
t.Fatalf("got unexpected respone data, exepcted %q, got %q", string(testImpl.respData), string(resp.buf)) | ||
} | ||
} | ||
} | ||
|
||
type testFdPayload struct { | ||
Fds []int64 `protobuf:"varint,1,opt,name=fds,proto3"` | ||
} | ||
|
||
func (r *testFdPayload) Reset() { *r = testFdPayload{} } | ||
func (r *testFdPayload) String() string { return fmt.Sprintf("%+#v", r) } | ||
func (r *testFdPayload) ProtoMessage() {} | ||
|
||
type testingServerFd struct { | ||
respData []byte | ||
} | ||
|
||
func (s *testingServerFd) Test(ctx context.Context, req *testFdPayload) error { | ||
for i, fd := range req.Fds { | ||
f := os.NewFile(uintptr(fd), "TEST_FILE_"+strconv.Itoa(i)) | ||
go func() { | ||
f.Write(s.respData) | ||
f.Close() | ||
}() | ||
} | ||
|
||
return nil | ||
} | ||
|
||
type testFdClient struct { | ||
client *Client | ||
} | ||
|
||
func (c *testFdClient) Test(ctx context.Context, files ...*os.File) error { | ||
fds, err := c.client.Sendfd(ctx, files) | ||
if err != nil { | ||
return fmt.Errorf("error sending fds: %w", err) | ||
} | ||
|
||
tp := testFdPayload{} | ||
return c.client.Call(ctx, "Test", "Test", &testFdPayload{Fds: fds}, &tp) | ||
} |
Oops, something went wrong.