support full urls (with eg tcp:// prefix)

This commit is contained in:
Ethan Buchman 2016-08-10 01:12:01 -04:00
parent dea910cd3e
commit 479510be0e
5 changed files with 65 additions and 32 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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"
)

View File

@ -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)
}

View File

@ -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