mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-17 02:10:46 +00:00
rpc: fix staticcheck warning SA1029 by add PeerInfo (#24255)
This commit is contained in:
parent
e52b8a2de2
commit
2844dbc5a9
10 changed files with 156 additions and 18 deletions
|
|
@ -112,6 +112,7 @@ type clientConn struct {
|
||||||
|
|
||||||
func (c *Client) newClientConn(conn ServerCodec) *clientConn {
|
func (c *Client) newClientConn(conn ServerCodec) *clientConn {
|
||||||
ctx := context.WithValue(context.Background(), clientContextKey{}, c)
|
ctx := context.WithValue(context.Background(), clientContextKey{}, c)
|
||||||
|
ctx = context.WithValue(ctx, peerInfoContextKey{}, conn.peerInfo())
|
||||||
handler := newHandler(ctx, conn, c.idgen, c.services)
|
handler := newHandler(ctx, conn, c.idgen, c.services)
|
||||||
return &clientConn{conn, handler}
|
return &clientConn{conn, handler}
|
||||||
}
|
}
|
||||||
|
|
@ -473,7 +474,7 @@ func (c *Client) Subscribe(ctx context.Context, namespace string, channel interf
|
||||||
// Check type of channel first.
|
// Check type of channel first.
|
||||||
chanVal := reflect.ValueOf(channel)
|
chanVal := reflect.ValueOf(channel)
|
||||||
if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 {
|
if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 {
|
||||||
panic("first argument to Subscribe must be a writable channel")
|
panic(fmt.Sprintf("channel argument of Subscribe has type %T, need writable channel", channel))
|
||||||
}
|
}
|
||||||
if chanVal.IsNil() {
|
if chanVal.IsNil() {
|
||||||
panic("channel given to Subscribe must not be nil")
|
panic("channel given to Subscribe must not be nil")
|
||||||
|
|
@ -532,8 +533,8 @@ func (c *Client) send(ctx context.Context, op *requestOp, msg interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) write(ctx context.Context, msg interface{}, retry bool) error {
|
func (c *Client) write(ctx context.Context, msg interface{}, retry bool) error {
|
||||||
// The previous write failed. Try to establish a new connection.
|
|
||||||
if c.writeConn == nil {
|
if c.writeConn == nil {
|
||||||
|
// The previous write failed. Try to establish a new connection.
|
||||||
if err := c.reconnect(ctx); err != nil {
|
if err := c.reconnect(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
rpc/http.go
30
rpc/http.go
|
|
@ -52,11 +52,18 @@ type httpConn struct {
|
||||||
headers http.Header
|
headers http.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
// httpConn is treated specially by Client.
|
// httpConn implements ServerCodec, but it is treated specially by Client
|
||||||
|
// and some methods don't work. The panic() stubs here exist to ensure
|
||||||
|
// this special treatment is correct.
|
||||||
|
|
||||||
func (hc *httpConn) writeJSON(context.Context, interface{}) error {
|
func (hc *httpConn) writeJSON(context.Context, interface{}) error {
|
||||||
panic("writeJSON called on httpConn")
|
panic("writeJSON called on httpConn")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hc *httpConn) peerInfo() PeerInfo {
|
||||||
|
panic("peerInfo called on httpConn")
|
||||||
|
}
|
||||||
|
|
||||||
func (hc *httpConn) remoteAddr() string {
|
func (hc *httpConn) remoteAddr() string {
|
||||||
return hc.url
|
return hc.url
|
||||||
}
|
}
|
||||||
|
|
@ -258,20 +265,19 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Error(w, err.Error(), code)
|
http.Error(w, err.Error(), code)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create request-scoped context.
|
||||||
|
connInfo := PeerInfo{Transport: "http", RemoteAddr: r.RemoteAddr}
|
||||||
|
connInfo.HTTP.Version = r.Proto
|
||||||
|
connInfo.HTTP.Host = r.Host
|
||||||
|
connInfo.HTTP.Origin = r.Header.Get("Origin")
|
||||||
|
connInfo.HTTP.UserAgent = r.Header.Get("User-Agent")
|
||||||
|
ctx := r.Context()
|
||||||
|
ctx = context.WithValue(ctx, peerInfoContextKey{}, connInfo)
|
||||||
|
|
||||||
// All checks passed, create a codec that reads directly from the request body
|
// All checks passed, create a codec that reads directly from the request body
|
||||||
// until EOF, writes the response to w, and orders the server to process a
|
// until EOF, writes the response to w, and orders the server to process a
|
||||||
// single request.
|
// single request.
|
||||||
ctx := r.Context()
|
|
||||||
ctx = context.WithValue(ctx, "remote", r.RemoteAddr)
|
|
||||||
ctx = context.WithValue(ctx, "scheme", r.Proto)
|
|
||||||
ctx = context.WithValue(ctx, "local", r.Host)
|
|
||||||
if ua := r.Header.Get("User-Agent"); ua != "" {
|
|
||||||
ctx = context.WithValue(ctx, "User-Agent", ua)
|
|
||||||
}
|
|
||||||
if origin := r.Header.Get("Origin"); origin != "" {
|
|
||||||
ctx = context.WithValue(ctx, "Origin", origin)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("content-type", contentType)
|
w.Header().Set("content-type", contentType)
|
||||||
codec := newHTTPServerConn(r, w)
|
codec := newHTTPServerConn(r, w)
|
||||||
defer codec.close()
|
defer codec.close()
|
||||||
|
|
|
||||||
|
|
@ -123,3 +123,39 @@ func TestHTTPRespBodyUnlimited(t *testing.T) {
|
||||||
t.Fatalf("response has wrong length %d, want %d", len(r), respLength)
|
t.Fatalf("response has wrong length %d, want %d", len(r), respLength)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHTTPPeerInfo(t *testing.T) {
|
||||||
|
s := newTestServer()
|
||||||
|
defer s.Stop()
|
||||||
|
ts := httptest.NewServer(s)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
c, err := Dial(ts.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
c.SetHeader("user-agent", "ua-testing")
|
||||||
|
c.SetHeader("origin", "origin.example.com")
|
||||||
|
|
||||||
|
// Request peer information.
|
||||||
|
var info PeerInfo
|
||||||
|
if err := c.Call(&info, "test_peerInfo"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.RemoteAddr == "" {
|
||||||
|
t.Error("RemoteAddr not set")
|
||||||
|
}
|
||||||
|
if info.Transport != "http" {
|
||||||
|
t.Errorf("wrong Transport %q", info.Transport)
|
||||||
|
}
|
||||||
|
if info.HTTP.Version != "HTTP/1.1" {
|
||||||
|
t.Errorf("wrong HTTP.Version %q", info.HTTP.Version)
|
||||||
|
}
|
||||||
|
if info.HTTP.UserAgent != "ua-testing" {
|
||||||
|
t.Errorf("wrong HTTP.UserAgent %q", info.HTTP.UserAgent)
|
||||||
|
}
|
||||||
|
if info.HTTP.Origin != "origin.example.com" {
|
||||||
|
t.Errorf("wrong HTTP.Origin %q", info.HTTP.UserAgent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,11 @@ func NewCodec(conn Conn) ServerCodec {
|
||||||
return NewFuncCodec(conn, enc.Encode, dec.Decode)
|
return NewFuncCodec(conn, enc.Encode, dec.Decode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *jsonCodec) peerInfo() PeerInfo {
|
||||||
|
// This returns "ipc" because all other built-in transports have a separate codec type.
|
||||||
|
return PeerInfo{Transport: "ipc", RemoteAddr: c.remote}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *jsonCodec) remoteAddr() string {
|
func (c *jsonCodec) remoteAddr() string {
|
||||||
return c.remote
|
return c.remote
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,3 +145,38 @@ func (s *RPCService) Modules() map[string]string {
|
||||||
}
|
}
|
||||||
return modules
|
return modules
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PeerInfo contains information about the remote end of the network connection.
|
||||||
|
//
|
||||||
|
// This is available within RPC method handlers through the context. Call
|
||||||
|
// PeerInfoFromContext to get information about the client connection related to
|
||||||
|
// the current method call.
|
||||||
|
type PeerInfo struct {
|
||||||
|
// Transport is name of the protocol used by the client.
|
||||||
|
// This can be "http", "ws" or "ipc".
|
||||||
|
Transport string
|
||||||
|
|
||||||
|
// Address of client. This will usually contain the IP address and port.
|
||||||
|
RemoteAddr string
|
||||||
|
|
||||||
|
// Addditional information for HTTP and WebSocket connections.
|
||||||
|
HTTP struct {
|
||||||
|
// Protocol version, i.e. "HTTP/1.1". This is not set for WebSocket.
|
||||||
|
Version string
|
||||||
|
// Header values sent by the client.
|
||||||
|
UserAgent string
|
||||||
|
Origin string
|
||||||
|
Host string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type peerInfoContextKey struct{}
|
||||||
|
|
||||||
|
// PeerInfoFromContext returns information about the client's network connection.
|
||||||
|
// Use this with the context passed to RPC method handler functions.
|
||||||
|
//
|
||||||
|
// The zero value is returned if no connection info is present in ctx.
|
||||||
|
func PeerInfoFromContext(ctx context.Context) PeerInfo {
|
||||||
|
info, _ := ctx.Value(peerInfoContextKey{}).(PeerInfo)
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ func TestServerRegisterName(t *testing.T) {
|
||||||
t.Fatalf("Expected service calc to be registered")
|
t.Fatalf("Expected service calc to be registered")
|
||||||
}
|
}
|
||||||
|
|
||||||
wantCallbacks := 10
|
wantCallbacks := 11
|
||||||
if len(svc.callbacks) != wantCallbacks {
|
if len(svc.callbacks) != wantCallbacks {
|
||||||
t.Errorf("Expected %d callbacks for service 'service', got %d", wantCallbacks, len(svc.callbacks))
|
t.Errorf("Expected %d callbacks for service 'service', got %d", wantCallbacks, len(svc.callbacks))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,10 @@ func (s *testService) EchoWithCtx(ctx context.Context, str string, i int, args *
|
||||||
return echoResult{str, i, args}
|
return echoResult{str, i, args}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *testService) PeerInfo(ctx context.Context) PeerInfo {
|
||||||
|
return PeerInfoFromContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *testService) Sleep(ctx context.Context, duration time.Duration) {
|
func (s *testService) Sleep(ctx context.Context, duration time.Duration) {
|
||||||
time.Sleep(duration)
|
time.Sleep(duration)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,10 @@ type DataError interface {
|
||||||
// a RPC session. Implementations must be go-routine safe since the codec can be called in
|
// a RPC session. Implementations must be go-routine safe since the codec can be called in
|
||||||
// multiple go-routines concurrently.
|
// multiple go-routines concurrently.
|
||||||
type ServerCodec interface {
|
type ServerCodec interface {
|
||||||
|
peerInfo() PeerInfo
|
||||||
readBatch() (msgs []*jsonrpcMessage, isBatch bool, err error)
|
readBatch() (msgs []*jsonrpcMessage, isBatch bool, err error)
|
||||||
close()
|
close()
|
||||||
|
|
||||||
jsonWriter
|
jsonWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ func (s *Server) WebsocketHandler(allowedOrigins []string) http.Handler {
|
||||||
log.Debug("WebSocket upgrade failed", "err", err)
|
log.Debug("WebSocket upgrade failed", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
codec := newWebsocketCodec(conn)
|
codec := newWebsocketCodec(conn, r.Host, r.Header)
|
||||||
s.ServeCodec(codec, 0)
|
s.ServeCodec(codec, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -203,7 +203,7 @@ func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, diale
|
||||||
}
|
}
|
||||||
return nil, hErr
|
return nil, hErr
|
||||||
}
|
}
|
||||||
return newWebsocketCodec(conn), nil
|
return newWebsocketCodec(conn, endpoint, header), nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,18 +241,28 @@ func wsClientHeaders(endpoint, origin string) (string, http.Header, error) {
|
||||||
type websocketCodec struct {
|
type websocketCodec struct {
|
||||||
*jsonCodec
|
*jsonCodec
|
||||||
conn *websocket.Conn
|
conn *websocket.Conn
|
||||||
|
info PeerInfo
|
||||||
|
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
pingReset chan struct{}
|
pingReset chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWebsocketCodec(conn *websocket.Conn) ServerCodec {
|
func newWebsocketCodec(conn *websocket.Conn, host string, req http.Header) ServerCodec {
|
||||||
conn.SetReadLimit(wsMessageSizeLimit)
|
conn.SetReadLimit(wsMessageSizeLimit)
|
||||||
wc := &websocketCodec{
|
wc := &websocketCodec{
|
||||||
jsonCodec: NewFuncCodec(conn, conn.WriteJSON, conn.ReadJSON).(*jsonCodec),
|
jsonCodec: NewFuncCodec(conn, conn.WriteJSON, conn.ReadJSON).(*jsonCodec),
|
||||||
conn: conn,
|
conn: conn,
|
||||||
pingReset: make(chan struct{}, 1),
|
pingReset: make(chan struct{}, 1),
|
||||||
|
info: PeerInfo{
|
||||||
|
Transport: "ws",
|
||||||
|
RemoteAddr: conn.RemoteAddr().String(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
// Fill in connection details.
|
||||||
|
wc.info.HTTP.Host = host
|
||||||
|
wc.info.HTTP.Origin = req.Get("Origin")
|
||||||
|
wc.info.HTTP.UserAgent = req.Get("User-Agent")
|
||||||
|
// Start pinger.
|
||||||
wc.wg.Add(1)
|
wc.wg.Add(1)
|
||||||
go wc.pingLoop()
|
go wc.pingLoop()
|
||||||
return wc
|
return wc
|
||||||
|
|
@ -263,6 +273,10 @@ func (wc *websocketCodec) close() {
|
||||||
wc.wg.Wait()
|
wc.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wc *websocketCodec) peerInfo() PeerInfo {
|
||||||
|
return wc.info
|
||||||
|
}
|
||||||
|
|
||||||
func (wc *websocketCodec) writeJSON(ctx context.Context, v interface{}) error {
|
func (wc *websocketCodec) writeJSON(ctx context.Context, v interface{}) error {
|
||||||
err := wc.jsonCodec.writeJSON(ctx, v)
|
err := wc.jsonCodec.writeJSON(ctx, v)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,41 @@ func TestWebsocketLargeCall(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWebsocketPeerInfo(t *testing.T) {
|
||||||
|
var (
|
||||||
|
s = newTestServer()
|
||||||
|
ts = httptest.NewServer(s.WebsocketHandler([]string{"origin.example.com"}))
|
||||||
|
tsurl = "ws:" + strings.TrimPrefix(ts.URL, "http:")
|
||||||
|
)
|
||||||
|
defer s.Stop()
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
c, err := DialWebsocket(ctx, tsurl, "origin.example.com")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request peer information.
|
||||||
|
var connInfo PeerInfo
|
||||||
|
if err := c.Call(&connInfo, "test_peerInfo"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if connInfo.RemoteAddr == "" {
|
||||||
|
t.Error("RemoteAddr not set")
|
||||||
|
}
|
||||||
|
if connInfo.Transport != "ws" {
|
||||||
|
t.Errorf("wrong Transport %q", connInfo.Transport)
|
||||||
|
}
|
||||||
|
if connInfo.HTTP.UserAgent != "Go-http-client/1.1" {
|
||||||
|
t.Errorf("wrong HTTP.UserAgent %q", connInfo.HTTP.UserAgent)
|
||||||
|
}
|
||||||
|
if connInfo.HTTP.Origin != "origin.example.com" {
|
||||||
|
t.Errorf("wrong HTTP.Origin %q", connInfo.HTTP.UserAgent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This test checks that client handles WebSocket ping frames correctly.
|
// This test checks that client handles WebSocket ping frames correctly.
|
||||||
func TestClientWebsocketPing(t *testing.T) {
|
func TestClientWebsocketPing(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue