forked from cerc-io/plugeth
rpc: add PeerInfo (#24255)
This replaces the sketchy and undocumented string context keys for HTTP requests with a defined interface. Using string keys with context is discouraged because they may clash with keys created by other packages. We added these keys to make connection metadata available in the signer, so this change also updates signer/core to use the new PeerInfo API.
This commit is contained in:
parent
514ae7cfa3
commit
5bcbb2980b
@ -58,12 +58,6 @@ const (
|
|||||||
maxClientSubscriptionBuffer = 20000
|
maxClientSubscriptionBuffer = 20000
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
httpScheme = "http"
|
|
||||||
wsScheme = "ws"
|
|
||||||
ipcScheme = "ipc"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BatchElem is an element in a batch request.
|
// BatchElem is an element in a batch request.
|
||||||
type BatchElem struct {
|
type BatchElem struct {
|
||||||
Method string
|
Method string
|
||||||
@ -80,7 +74,7 @@ type BatchElem struct {
|
|||||||
// Client represents a connection to an RPC server.
|
// Client represents a connection to an RPC server.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
idgen func() ID // for subscriptions
|
idgen func() ID // for subscriptions
|
||||||
scheme string // connection type: http, ws or ipc
|
isHTTP bool // connection type: http, ws or ipc
|
||||||
services *serviceRegistry
|
services *serviceRegistry
|
||||||
|
|
||||||
idCounter uint32
|
idCounter uint32
|
||||||
@ -115,11 +109,9 @@ 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.Background()
|
||||||
// Http connections have already set the scheme
|
ctx = context.WithValue(ctx, clientContextKey{}, c)
|
||||||
if !c.isHTTP() && c.scheme != "" {
|
ctx = context.WithValue(ctx, peerInfoContextKey{}, conn.peerInfo())
|
||||||
ctx = context.WithValue(ctx, "scheme", c.scheme)
|
|
||||||
}
|
|
||||||
handler := newHandler(ctx, conn, c.idgen, c.services)
|
handler := newHandler(ctx, conn, c.idgen, c.services)
|
||||||
return &clientConn{conn, handler}
|
return &clientConn{conn, handler}
|
||||||
}
|
}
|
||||||
@ -145,7 +137,7 @@ func (op *requestOp) wait(ctx context.Context, c *Client) (*jsonrpcMessage, erro
|
|||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// Send the timeout to dispatch so it can remove the request IDs.
|
// Send the timeout to dispatch so it can remove the request IDs.
|
||||||
if !c.isHTTP() {
|
if !c.isHTTP {
|
||||||
select {
|
select {
|
||||||
case c.reqTimeout <- op:
|
case c.reqTimeout <- op:
|
||||||
case <-c.closing:
|
case <-c.closing:
|
||||||
@ -212,18 +204,10 @@ func newClient(initctx context.Context, connect reconnectFunc) (*Client, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initClient(conn ServerCodec, idgen func() ID, services *serviceRegistry) *Client {
|
func initClient(conn ServerCodec, idgen func() ID, services *serviceRegistry) *Client {
|
||||||
scheme := ""
|
_, isHTTP := conn.(*httpConn)
|
||||||
switch conn.(type) {
|
|
||||||
case *httpConn:
|
|
||||||
scheme = httpScheme
|
|
||||||
case *websocketCodec:
|
|
||||||
scheme = wsScheme
|
|
||||||
case *jsonCodec:
|
|
||||||
scheme = ipcScheme
|
|
||||||
}
|
|
||||||
c := &Client{
|
c := &Client{
|
||||||
|
isHTTP: isHTTP,
|
||||||
idgen: idgen,
|
idgen: idgen,
|
||||||
scheme: scheme,
|
|
||||||
services: services,
|
services: services,
|
||||||
writeConn: conn,
|
writeConn: conn,
|
||||||
close: make(chan struct{}),
|
close: make(chan struct{}),
|
||||||
@ -236,7 +220,7 @@ func initClient(conn ServerCodec, idgen func() ID, services *serviceRegistry) *C
|
|||||||
reqSent: make(chan error, 1),
|
reqSent: make(chan error, 1),
|
||||||
reqTimeout: make(chan *requestOp),
|
reqTimeout: make(chan *requestOp),
|
||||||
}
|
}
|
||||||
if !c.isHTTP() {
|
if !isHTTP {
|
||||||
go c.dispatch(conn)
|
go c.dispatch(conn)
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
@ -267,7 +251,7 @@ func (c *Client) SupportedModules() (map[string]string, error) {
|
|||||||
|
|
||||||
// Close closes the client, aborting any in-flight requests.
|
// Close closes the client, aborting any in-flight requests.
|
||||||
func (c *Client) Close() {
|
func (c *Client) Close() {
|
||||||
if c.isHTTP() {
|
if c.isHTTP {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
@ -281,7 +265,7 @@ func (c *Client) Close() {
|
|||||||
// This method only works for clients using HTTP, it doesn't have
|
// This method only works for clients using HTTP, it doesn't have
|
||||||
// any effect for clients using another transport.
|
// any effect for clients using another transport.
|
||||||
func (c *Client) SetHeader(key, value string) {
|
func (c *Client) SetHeader(key, value string) {
|
||||||
if !c.isHTTP() {
|
if !c.isHTTP {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
conn := c.writeConn.(*httpConn)
|
conn := c.writeConn.(*httpConn)
|
||||||
@ -315,7 +299,7 @@ func (c *Client) CallContext(ctx context.Context, result interface{}, method str
|
|||||||
}
|
}
|
||||||
op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)}
|
op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)}
|
||||||
|
|
||||||
if c.isHTTP() {
|
if c.isHTTP {
|
||||||
err = c.sendHTTP(ctx, op, msg)
|
err = c.sendHTTP(ctx, op, msg)
|
||||||
} else {
|
} else {
|
||||||
err = c.send(ctx, op, msg)
|
err = c.send(ctx, op, msg)
|
||||||
@ -378,7 +362,7 @@ func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if c.isHTTP() {
|
if c.isHTTP {
|
||||||
err = c.sendBatchHTTP(ctx, op, msgs)
|
err = c.sendBatchHTTP(ctx, op, msgs)
|
||||||
} else {
|
} else {
|
||||||
err = c.send(ctx, op, msgs)
|
err = c.send(ctx, op, msgs)
|
||||||
@ -417,7 +401,7 @@ func (c *Client) Notify(ctx context.Context, method string, args ...interface{})
|
|||||||
}
|
}
|
||||||
msg.ID = nil
|
msg.ID = nil
|
||||||
|
|
||||||
if c.isHTTP() {
|
if c.isHTTP {
|
||||||
return c.sendHTTP(ctx, op, msg)
|
return c.sendHTTP(ctx, op, msg)
|
||||||
}
|
}
|
||||||
return c.send(ctx, op, msg)
|
return c.send(ctx, op, msg)
|
||||||
@ -450,12 +434,12 @@ 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")
|
||||||
}
|
}
|
||||||
if c.isHTTP() {
|
if c.isHTTP {
|
||||||
return nil, ErrNotificationsUnsupported
|
return nil, ErrNotificationsUnsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,8 +493,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
|
||||||
}
|
}
|
||||||
@ -657,7 +641,3 @@ func (c *Client) read(codec ServerCodec) {
|
|||||||
c.readOp <- readOp{msgs, batch}
|
c.readOp <- readOp{msgs, batch}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) isHTTP() bool {
|
|
||||||
return c.scheme == httpScheme
|
|
||||||
}
|
|
||||||
|
30
rpc/http.go
30
rpc/http.go
@ -48,11 +48,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
|
||||||
}
|
}
|
||||||
@ -236,20 +243,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()
|
||||||
|
@ -162,3 +162,39 @@ func TestHTTPErrorResponse(t *testing.T) {
|
|||||||
t.Error("unexpected error message", errMsg)
|
t.Error("unexpected error message", errMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 := 9
|
wantCallbacks := 10
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,10 @@ type API struct {
|
|||||||
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -197,7 +197,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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,12 +235,13 @@ 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)
|
||||||
conn.SetPongHandler(func(appData string) error {
|
conn.SetPongHandler(func(appData string) error {
|
||||||
conn.SetReadDeadline(time.Time{})
|
conn.SetReadDeadline(time.Time{})
|
||||||
@ -250,7 +251,16 @@ func newWebsocketCodec(conn *websocket.Conn) ServerCodec {
|
|||||||
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
|
||||||
@ -261,6 +271,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 {
|
||||||
|
@ -117,6 +117,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()
|
||||||
|
@ -33,6 +33,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||||
"github.com/ethereum/go-ethereum/signer/storage"
|
"github.com/ethereum/go-ethereum/signer/storage"
|
||||||
)
|
)
|
||||||
@ -188,23 +189,24 @@ func StartClefAccountManager(ksLocation string, nousb, lightKDF bool, scpath str
|
|||||||
|
|
||||||
// MetadataFromContext extracts Metadata from a given context.Context
|
// MetadataFromContext extracts Metadata from a given context.Context
|
||||||
func MetadataFromContext(ctx context.Context) Metadata {
|
func MetadataFromContext(ctx context.Context) Metadata {
|
||||||
|
info := rpc.PeerInfoFromContext(ctx)
|
||||||
|
|
||||||
m := Metadata{"NA", "NA", "NA", "", ""} // batman
|
m := Metadata{"NA", "NA", "NA", "", ""} // batman
|
||||||
|
|
||||||
if v := ctx.Value("remote"); v != nil {
|
if info.Transport != "" {
|
||||||
m.Remote = v.(string)
|
if info.Transport == "http" {
|
||||||
|
m.Scheme = info.HTTP.Version
|
||||||
|
}
|
||||||
|
m.Scheme = info.Transport
|
||||||
}
|
}
|
||||||
if v := ctx.Value("scheme"); v != nil {
|
if info.RemoteAddr != "" {
|
||||||
m.Scheme = v.(string)
|
m.Remote = info.RemoteAddr
|
||||||
}
|
}
|
||||||
if v := ctx.Value("local"); v != nil {
|
if info.HTTP.Host != "" {
|
||||||
m.Local = v.(string)
|
m.Local = info.HTTP.Host
|
||||||
}
|
|
||||||
if v := ctx.Value("Origin"); v != nil {
|
|
||||||
m.Origin = v.(string)
|
|
||||||
}
|
|
||||||
if v := ctx.Value("User-Agent"); v != nil {
|
|
||||||
m.UserAgent = v.(string)
|
|
||||||
}
|
}
|
||||||
|
m.Origin = info.HTTP.Origin
|
||||||
|
m.UserAgent = info.HTTP.UserAgent
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user