From 479510be0e80dd9e5d6b1f941adad168df0af85f Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 10 Aug 2016 01:12:01 -0400 Subject: [PATCH] support full urls (with eg tcp:// prefix) --- client/http_client.go | 58 +++++++++++++++++++++++++++---------------- client/ws_client.go | 10 +++++--- rpc_test.go | 4 +-- server/http_server.go | 23 +++++++++++++---- version.go | 2 +- 5 files changed, 65 insertions(+), 32 deletions(-) diff --git a/client/http_client.go b/client/http_client.go index 7c0480600..63dd9a82d 100644 --- a/client/http_client.go +++ b/client/http_client.go @@ -8,26 +8,40 @@ import ( "net" "net/http" "net/url" + "strings" . "github.com/tendermint/go-common" "github.com/tendermint/go-rpc/types" "github.com/tendermint/go-wire" ) -// Set the net.Dial manually so we can do http over tcp or unix. -// Get/Post require a dummyDomain but it's over written by the Transport -var dummyDomain = "http://dummyDomain" +// TODO: Deprecate support for IP:PORT or /path/to/socket +func makeHTTPDialer(remoteAddr string) (string, func(string, string) (net.Conn, error)) { -func dialer(remote string) func(string, string) (net.Conn, error) { - return func(proto, addr string) (conn net.Conn, err error) { - return net.Dial(rpctypes.SocketType(remote), remote) + parts := strings.SplitN(remoteAddr, "://", 2) + var protocol, address string + if len(parts) != 2 { + log.Warn("WARNING (go-rpc): Please use fully formed listening addresses, including the tcp:// or unix:// prefix") + protocol = rpctypes.SocketType(remoteAddr) + address = remoteAddr + } else { + protocol, address = parts[0], parts[1] + } + + trimmedAddress := strings.Replace(address, "/", ".", -1) // replace / with . for http requests (dummy domain) + return trimmedAddress, func(proto, addr string) (net.Conn, error) { + return net.Dial(protocol, address) } } -// remote is IP:PORT or /path/to/socket -func socketTransport(remote string) *http.Transport { - return &http.Transport{ - Dial: dialer(remote), +// We overwrite the http.Client.Dial so we can do http over tcp or unix. +// remoteAddr should be fully featured (eg. with tcp:// or unix://) +func makeHTTPClient(remoteAddr string) (string, *http.Client) { + address, dialer := makeHTTPDialer(remoteAddr) + return "http://" + address, &http.Client{ + Transport: &http.Transport{ + Dial: dialer, + }, } } @@ -40,14 +54,15 @@ type Client interface { // JSON rpc takes params as a slice type ClientJSONRPC struct { - remote string - client *http.Client + address string + client *http.Client } func NewClientJSONRPC(remote string) *ClientJSONRPC { + address, client := makeHTTPClient(remote) return &ClientJSONRPC{ - remote: remote, - client: &http.Client{Transport: socketTransport(remote)}, + address: address, + client: client, } } @@ -66,7 +81,7 @@ func (c *ClientJSONRPC) call(method string, params []interface{}, result interfa requestBytes := wire.JSONBytes(request) requestBuf := bytes.NewBuffer(requestBytes) // log.Info(Fmt("RPC request to %v (%v): %v", c.remote, method, string(requestBytes))) - httpResponse, err := c.client.Post(dummyDomain, "text/json", requestBuf) + httpResponse, err := c.client.Post(c.address, "text/json", requestBuf) if err != nil { return nil, err } @@ -83,14 +98,15 @@ func (c *ClientJSONRPC) call(method string, params []interface{}, result interfa // URI takes params as a map type ClientURI struct { - remote string - client *http.Client + address string + client *http.Client } func NewClientURI(remote string) *ClientURI { + address, client := makeHTTPClient(remote) return &ClientURI{ - remote: remote, - client: &http.Client{Transport: socketTransport(remote)}, + address: address, + client: client, } } @@ -103,8 +119,8 @@ func (c *ClientURI) call(method string, params map[string]interface{}, result in if err != nil { return nil, err } - log.Info(Fmt("URI request to %v (%v): %v", c.remote, method, values)) - resp, err := c.client.PostForm(dummyDomain+"/"+method, values) + log.Info(Fmt("URI request to %v (%v): %v", c.address, method, values)) + resp, err := c.client.PostForm(c.address+"/"+method, values) if err != nil { return nil, err } diff --git a/client/ws_client.go b/client/ws_client.go index a8b8ce80c..00e4222ad 100644 --- a/client/ws_client.go +++ b/client/ws_client.go @@ -3,6 +3,7 @@ package rpcclient import ( "encoding/json" "fmt" + "net" "net/http" "time" @@ -21,15 +22,18 @@ type WSClient struct { QuitService Address string // IP:PORT or /path/to/socket Endpoint string // /websocket/url/endpoint + Dialer func(string, string) (net.Conn, error) *websocket.Conn ResultsCh chan json.RawMessage // closes upon WSClient.Stop() ErrorsCh chan error // closes upon WSClient.Stop() } // create a new connection -func NewWSClient(addr, endpoint string) *WSClient { +func NewWSClient(remoteAddr, endpoint string) *WSClient { + addr, dialer := makeHTTPDialer(remoteAddr) wsClient := &WSClient{ Address: addr, + Dialer: dialer, Endpoint: endpoint, Conn: nil, ResultsCh: make(chan json.RawMessage, wsResultsChannelCapacity), @@ -57,11 +61,11 @@ func (wsc *WSClient) dial() error { // Dial dialer := &websocket.Dialer{ - NetDial: dialer(wsc.Address), + NetDial: wsc.Dialer, Proxy: http.ProxyFromEnvironment, } rHeader := http.Header{} - con, _, err := dialer.Dial("ws://"+dummyDomain+wsc.Endpoint, rHeader) + con, _, err := dialer.Dial("ws://"+wsc.Address+wsc.Endpoint, rHeader) if err != nil { return err } diff --git a/rpc_test.go b/rpc_test.go index 82bdeb136..0c6e1dbdb 100644 --- a/rpc_test.go +++ b/rpc_test.go @@ -13,8 +13,8 @@ import ( // Client and Server should work over tcp or unix sockets var ( - tcpAddr = "0.0.0.0:46657" - unixAddr = "/tmp/go-rpc.sock" // NOTE: must remove file for test to run again + tcpAddr = "tcp://0.0.0.0:46657" + unixAddr = "unix:///tmp/go-rpc.sock" // NOTE: must remove file for test to run again websocketEndpoint = "/websocket/endpoint" ) diff --git a/server/http_server.go b/server/http_server.go index cc3da39be..26163cf12 100644 --- a/server/http_server.go +++ b/server/http_server.go @@ -8,6 +8,7 @@ import ( "net" "net/http" "runtime/debug" + "strings" "time" . "github.com/tendermint/go-common" @@ -15,11 +16,23 @@ import ( //"github.com/tendermint/go-wire" ) -func StartHTTPServer(listenAddr string, handler http.Handler) (net.Listener, error) { - // listenAddr is `IP:PORT` or /path/to/socket - socketType := SocketType(listenAddr) - log.Notice(Fmt("Starting RPC HTTP server on %s socket %v", socketType, listenAddr)) - listener, err := net.Listen(socketType, listenAddr) +func StartHTTPServer(listenAddr string, handler http.Handler) (listener net.Listener, err error) { + // listenAddr should be fully formed including tcp:// or unix:// prefix + var proto, addr string + parts := strings.SplitN(listenAddr, "://", 2) + if len(parts) != 2 { + log.Warn("WARNING (go-rpc): Please use fully formed listening addresses, including the tcp:// or unix:// prefix") + // we used to allow addrs without tcp/unix prefix by checking for a colon + // TODO: Deprecate + proto = SocketType(listenAddr) + addr = listenAddr + // return nil, fmt.Errorf("Invalid listener address %s", lisenAddr) + } else { + proto, addr = parts[0], parts[1] + } + + log.Notice(Fmt("Starting RPC HTTP server on %s socket %v", proto, addr)) + listener, err = net.Listen(proto, addr) if err != nil { return nil, fmt.Errorf("Failed to listen to %v: %v", listenAddr, err) } diff --git a/version.go b/version.go index d0a0ee9cd..edf8d40da 100644 --- a/version.go +++ b/version.go @@ -2,6 +2,6 @@ package rpc const Maj = "0" const Min = "5" // refactored out of tendermint/tendermint; RPCResponse.Result is RawJSON -const Fix = "0" +const Fix = "1" // support tcp:// or unix:// prefixes const Version = Maj + "." + Min + "." + Fix