diff --git a/rpc/jsonrpc/server/http_json_handler.go b/rpc/jsonrpc/server/http_json_handler.go index fbc0cca79..b2ccf8694 100644 --- a/rpc/jsonrpc/server/http_json_handler.go +++ b/rpc/jsonrpc/server/http_json_handler.go @@ -1,14 +1,14 @@ package server import ( - "bytes" "encoding/json" "errors" "fmt" + "html/template" "io/ioutil" "net/http" "reflect" - "sort" + "strings" tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" @@ -239,42 +239,27 @@ func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte) ([]reflect.Value, error) { // writes a list of available rpc endpoints as an html page func writeListOfEndpoints(w http.ResponseWriter, r *http.Request, funcMap map[string]*RPCFunc) { - noArgNames := []string{} - argNames := []string{} - for name, funcData := range funcMap { - if len(funcData.args) == 0 { - noArgNames = append(noArgNames, name) + hasArgs := make(map[string]string) + noArgs := make(map[string]string) + for name, rf := range funcMap { + base := fmt.Sprintf("//%s/%s", r.Host, name) + // N.B. Check argNames, not args, since the type list includes the type + // of the leading context argument. + if len(rf.argNames) == 0 { + noArgs[name] = base } else { - argNames = append(argNames, name) - } - } - sort.Strings(noArgNames) - sort.Strings(argNames) - buf := new(bytes.Buffer) - buf.WriteString("") - buf.WriteString("
Available endpoints:
") - - for _, name := range noArgNames { - link := fmt.Sprintf("//%s/%s", r.Host, name) - buf.WriteString(fmt.Sprintf("%s
", link, link)) - } - - buf.WriteString("
Endpoints that require arguments:
") - for _, name := range argNames { - link := fmt.Sprintf("//%s/%s?", r.Host, name) - funcData := funcMap[name] - for i, argName := range funcData.argNames { - link += argName + "=_" - if i < len(funcData.argNames)-1 { - link += "&" + query := append([]string(nil), rf.argNames...) + for i, arg := range query { + query[i] = arg + "=_" } + hasArgs[name] = base + "?" + strings.Join(query, "&") } - buf.WriteString(fmt.Sprintf("%s
", link, link)) } - buf.WriteString("") w.Header().Set("Content-Type", "text/html") - w.WriteHeader(200) - w.Write(buf.Bytes()) // nolint: errcheck + _ = listOfEndpoints.Execute(w, map[string]map[string]string{ + "NoArgs": noArgs, + "HasArgs": hasArgs, + }) } func hasDefaultHeight(r rpctypes.RPCRequest, h []reflect.Value) bool { @@ -285,3 +270,29 @@ func hasDefaultHeight(r rpctypes.RPCRequest, h []reflect.Value) bool { return false } } + +var listOfEndpoints = template.Must(template.New("list").Parse(` +List of RPC Endpoints + + +

Available RPC endpoints:

+ +{{if .NoArgs}} +
+

Endpoints with no arguments:

+ +{{end}} + +{{if .HasArgs}} +
+

Endpoints that require arguments:

+ +{{end}} + +`))