You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

669 lines
20 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. package rpcserver
  2. import (
  3. "bytes"
  4. "encoding/hex"
  5. "encoding/json"
  6. "fmt"
  7. "io/ioutil"
  8. "net/http"
  9. "reflect"
  10. "sort"
  11. "strings"
  12. "time"
  13. "github.com/gorilla/websocket"
  14. "github.com/pkg/errors"
  15. types "github.com/tendermint/tendermint/rpc/lib/types"
  16. cmn "github.com/tendermint/tmlibs/common"
  17. events "github.com/tendermint/tmlibs/events"
  18. "github.com/tendermint/tmlibs/log"
  19. )
  20. // Adds a route for each function in the funcMap, as well as general jsonrpc and websocket handlers for all functions.
  21. // "result" is the interface on which the result objects are registered, and is popualted with every RPCResponse
  22. func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger log.Logger) {
  23. // HTTP endpoints
  24. for funcName, rpcFunc := range funcMap {
  25. mux.HandleFunc("/"+funcName, makeHTTPHandler(rpcFunc, logger))
  26. }
  27. // JSONRPC endpoints
  28. mux.HandleFunc("/", makeJSONRPCHandler(funcMap, logger))
  29. }
  30. //-------------------------------------
  31. // function introspection
  32. // holds all type information for each function
  33. type RPCFunc struct {
  34. f reflect.Value // underlying rpc function
  35. args []reflect.Type // type of each function arg
  36. returns []reflect.Type // type of each return arg
  37. argNames []string // name of each argument
  38. ws bool // websocket only
  39. }
  40. // wraps a function for quicker introspection
  41. // f is the function, args are comma separated argument names
  42. func NewRPCFunc(f interface{}, args string) *RPCFunc {
  43. return newRPCFunc(f, args, false)
  44. }
  45. func NewWSRPCFunc(f interface{}, args string) *RPCFunc {
  46. return newRPCFunc(f, args, true)
  47. }
  48. func newRPCFunc(f interface{}, args string, ws bool) *RPCFunc {
  49. var argNames []string
  50. if args != "" {
  51. argNames = strings.Split(args, ",")
  52. }
  53. return &RPCFunc{
  54. f: reflect.ValueOf(f),
  55. args: funcArgTypes(f),
  56. returns: funcReturnTypes(f),
  57. argNames: argNames,
  58. ws: ws,
  59. }
  60. }
  61. // return a function's argument types
  62. func funcArgTypes(f interface{}) []reflect.Type {
  63. t := reflect.TypeOf(f)
  64. n := t.NumIn()
  65. typez := make([]reflect.Type, n)
  66. for i := 0; i < n; i++ {
  67. typez[i] = t.In(i)
  68. }
  69. return typez
  70. }
  71. // return a function's return types
  72. func funcReturnTypes(f interface{}) []reflect.Type {
  73. t := reflect.TypeOf(f)
  74. n := t.NumOut()
  75. typez := make([]reflect.Type, n)
  76. for i := 0; i < n; i++ {
  77. typez[i] = t.Out(i)
  78. }
  79. return typez
  80. }
  81. // function introspection
  82. //-----------------------------------------------------------------------------
  83. // rpc.json
  84. // jsonrpc calls grab the given method's function info and runs reflect.Call
  85. func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.HandlerFunc {
  86. return func(w http.ResponseWriter, r *http.Request) {
  87. b, _ := ioutil.ReadAll(r.Body)
  88. // if its an empty request (like from a browser),
  89. // just display a list of functions
  90. if len(b) == 0 {
  91. writeListOfEndpoints(w, r, funcMap)
  92. return
  93. }
  94. var request types.RPCRequest
  95. err := json.Unmarshal(b, &request)
  96. if err != nil {
  97. WriteRPCResponseHTTPError(w, http.StatusBadRequest, types.NewRPCResponse("", nil, fmt.Sprintf("Error unmarshalling request: %v", err.Error())))
  98. return
  99. }
  100. if len(r.URL.Path) > 1 {
  101. WriteRPCResponseHTTPError(w, http.StatusNotFound, types.NewRPCResponse(request.ID, nil, fmt.Sprintf("Invalid JSONRPC endpoint %s", r.URL.Path)))
  102. return
  103. }
  104. rpcFunc := funcMap[request.Method]
  105. if rpcFunc == nil {
  106. WriteRPCResponseHTTPError(w, http.StatusNotFound, types.NewRPCResponse(request.ID, nil, "RPC method unknown: "+request.Method))
  107. return
  108. }
  109. if rpcFunc.ws {
  110. WriteRPCResponseHTTPError(w, http.StatusMethodNotAllowed, types.NewRPCResponse(request.ID, nil, "RPC method is only for websockets: "+request.Method))
  111. return
  112. }
  113. args, err := jsonParamsToArgsRPC(rpcFunc, request.Params)
  114. if err != nil {
  115. WriteRPCResponseHTTPError(w, http.StatusBadRequest, types.NewRPCResponse(request.ID, nil, fmt.Sprintf("Error converting json params to arguments: %v", err.Error())))
  116. return
  117. }
  118. returns := rpcFunc.f.Call(args)
  119. logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns)
  120. result, err := unreflectResult(returns)
  121. if err != nil {
  122. WriteRPCResponseHTTPError(w, http.StatusInternalServerError, types.NewRPCResponse(request.ID, result, err.Error()))
  123. return
  124. }
  125. WriteRPCResponseHTTP(w, types.NewRPCResponse(request.ID, result, ""))
  126. }
  127. }
  128. func mapParamsToArgs(rpcFunc *RPCFunc, params map[string]*json.RawMessage, argsOffset int) ([]reflect.Value, error) {
  129. values := make([]reflect.Value, len(rpcFunc.argNames))
  130. for i, argName := range rpcFunc.argNames {
  131. argType := rpcFunc.args[i+argsOffset]
  132. if p, ok := params[argName]; ok && p != nil && len(*p) > 0 {
  133. val := reflect.New(argType)
  134. err := json.Unmarshal(*p, val.Interface())
  135. if err != nil {
  136. return nil, err
  137. }
  138. values[i] = val.Elem()
  139. } else { // use default for that type
  140. values[i] = reflect.Zero(argType)
  141. }
  142. }
  143. return values, nil
  144. }
  145. func arrayParamsToArgs(rpcFunc *RPCFunc, params []*json.RawMessage, argsOffset int) ([]reflect.Value, error) {
  146. if len(rpcFunc.argNames) != len(params) {
  147. return nil, errors.Errorf("Expected %v parameters (%v), got %v (%v)",
  148. len(rpcFunc.argNames), rpcFunc.argNames, len(params), params)
  149. }
  150. values := make([]reflect.Value, len(params))
  151. for i, p := range params {
  152. argType := rpcFunc.args[i+argsOffset]
  153. val := reflect.New(argType)
  154. err := json.Unmarshal(*p, val.Interface())
  155. if err != nil {
  156. return nil, err
  157. }
  158. values[i] = val.Elem()
  159. }
  160. return values, nil
  161. }
  162. // raw is unparsed json (from json.RawMessage) encoding either a map or an array.
  163. //
  164. // argsOffset should be 0 for RPC calls, and 1 for WS requests, where len(rpcFunc.args) != len(rpcFunc.argNames).
  165. // Example:
  166. // rpcFunc.args = [rpctypes.WSRPCContext string]
  167. // rpcFunc.argNames = ["arg"]
  168. func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte, argsOffset int) ([]reflect.Value, error) {
  169. // first, try to get the map..
  170. var m map[string]*json.RawMessage
  171. err := json.Unmarshal(raw, &m)
  172. if err == nil {
  173. return mapParamsToArgs(rpcFunc, m, argsOffset)
  174. }
  175. // otherwise, try an array
  176. var a []*json.RawMessage
  177. err = json.Unmarshal(raw, &a)
  178. if err == nil {
  179. return arrayParamsToArgs(rpcFunc, a, argsOffset)
  180. }
  181. // otherwise, bad format, we cannot parse
  182. return nil, errors.Errorf("Unknown type for JSON params: %v. Expected map or array", err)
  183. }
  184. // Convert a []interface{} OR a map[string]interface{} to properly typed values
  185. func jsonParamsToArgsRPC(rpcFunc *RPCFunc, params *json.RawMessage) ([]reflect.Value, error) {
  186. return jsonParamsToArgs(rpcFunc, *params, 0)
  187. }
  188. // Same as above, but with the first param the websocket connection
  189. func jsonParamsToArgsWS(rpcFunc *RPCFunc, params *json.RawMessage, wsCtx types.WSRPCContext) ([]reflect.Value, error) {
  190. values, err := jsonParamsToArgs(rpcFunc, *params, 1)
  191. if err != nil {
  192. return nil, err
  193. }
  194. return append([]reflect.Value{reflect.ValueOf(wsCtx)}, values...), nil
  195. }
  196. // rpc.json
  197. //-----------------------------------------------------------------------------
  198. // rpc.http
  199. // convert from a function name to the http handler
  200. func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWriter, *http.Request) {
  201. // Exception for websocket endpoints
  202. if rpcFunc.ws {
  203. return func(w http.ResponseWriter, r *http.Request) {
  204. WriteRPCResponseHTTPError(w, http.StatusMethodNotAllowed, types.NewRPCResponse("", nil, "This RPC method is only for websockets"))
  205. }
  206. }
  207. // All other endpoints
  208. return func(w http.ResponseWriter, r *http.Request) {
  209. logger.Debug("HTTP HANDLER", "req", r)
  210. args, err := httpParamsToArgs(rpcFunc, r)
  211. if err != nil {
  212. WriteRPCResponseHTTPError(w, http.StatusBadRequest, types.NewRPCResponse("", nil, fmt.Sprintf("Error converting http params to args: %v", err.Error())))
  213. return
  214. }
  215. returns := rpcFunc.f.Call(args)
  216. logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns)
  217. result, err := unreflectResult(returns)
  218. if err != nil {
  219. WriteRPCResponseHTTPError(w, http.StatusInternalServerError, types.NewRPCResponse("", nil, err.Error()))
  220. return
  221. }
  222. WriteRPCResponseHTTP(w, types.NewRPCResponse("", result, ""))
  223. }
  224. }
  225. // Covert an http query to a list of properly typed values.
  226. // To be properly decoded the arg must be a concrete type from tendermint (if its an interface).
  227. func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error) {
  228. values := make([]reflect.Value, len(rpcFunc.args))
  229. for i, name := range rpcFunc.argNames {
  230. argType := rpcFunc.args[i]
  231. values[i] = reflect.Zero(argType) // set default for that type
  232. arg := GetParam(r, name)
  233. // log.Notice("param to arg", "argType", argType, "name", name, "arg", arg)
  234. if "" == arg {
  235. continue
  236. }
  237. v, err, ok := nonJsonToArg(argType, arg)
  238. if err != nil {
  239. return nil, err
  240. }
  241. if ok {
  242. values[i] = v
  243. continue
  244. }
  245. values[i], err = _jsonStringToArg(argType, arg)
  246. if err != nil {
  247. return nil, err
  248. }
  249. }
  250. return values, nil
  251. }
  252. func _jsonStringToArg(ty reflect.Type, arg string) (reflect.Value, error) {
  253. v := reflect.New(ty)
  254. err := json.Unmarshal([]byte(arg), v.Interface())
  255. if err != nil {
  256. return v, err
  257. }
  258. v = v.Elem()
  259. return v, nil
  260. }
  261. func nonJsonToArg(ty reflect.Type, arg string) (reflect.Value, error, bool) {
  262. isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`)
  263. isHexString := strings.HasPrefix(strings.ToLower(arg), "0x")
  264. expectingString := ty.Kind() == reflect.String
  265. expectingByteSlice := ty.Kind() == reflect.Slice && ty.Elem().Kind() == reflect.Uint8
  266. if isHexString {
  267. if !expectingString && !expectingByteSlice {
  268. err := errors.Errorf("Got a hex string arg, but expected '%s'",
  269. ty.Kind().String())
  270. return reflect.ValueOf(nil), err, false
  271. }
  272. var value []byte
  273. value, err := hex.DecodeString(arg[2:])
  274. if err != nil {
  275. return reflect.ValueOf(nil), err, false
  276. }
  277. if ty.Kind() == reflect.String {
  278. return reflect.ValueOf(string(value)), nil, true
  279. }
  280. return reflect.ValueOf([]byte(value)), nil, true
  281. }
  282. if isQuotedString && expectingByteSlice {
  283. v := reflect.New(reflect.TypeOf(""))
  284. err := json.Unmarshal([]byte(arg), v.Interface())
  285. if err != nil {
  286. return reflect.ValueOf(nil), err, false
  287. }
  288. v = v.Elem()
  289. return reflect.ValueOf([]byte(v.String())), nil, true
  290. }
  291. return reflect.ValueOf(nil), nil, false
  292. }
  293. // rpc.http
  294. //-----------------------------------------------------------------------------
  295. // rpc.websocket
  296. const (
  297. writeChanCapacity = 1000
  298. wsWriteTimeoutSeconds = 30 // each write times out after this.
  299. wsReadTimeoutSeconds = 30 // connection times out if we haven't received *anything* in this long, not even pings.
  300. wsPingTickerSeconds = 10 // send a ping every PingTickerSeconds.
  301. )
  302. // a single websocket connection
  303. // contains listener id, underlying ws connection,
  304. // and the event switch for subscribing to events
  305. type wsConnection struct {
  306. cmn.BaseService
  307. remoteAddr string
  308. baseConn *websocket.Conn
  309. writeChan chan types.RPCResponse
  310. readTimeout *time.Timer
  311. pingTicker *time.Ticker
  312. funcMap map[string]*RPCFunc
  313. evsw events.EventSwitch
  314. }
  315. // new websocket connection wrapper
  316. func NewWSConnection(baseConn *websocket.Conn, funcMap map[string]*RPCFunc, evsw events.EventSwitch) *wsConnection {
  317. wsc := &wsConnection{
  318. remoteAddr: baseConn.RemoteAddr().String(),
  319. baseConn: baseConn,
  320. writeChan: make(chan types.RPCResponse, writeChanCapacity), // error when full.
  321. funcMap: funcMap,
  322. evsw: evsw,
  323. }
  324. wsc.BaseService = *cmn.NewBaseService(nil, "wsConnection", wsc)
  325. return wsc
  326. }
  327. // wsc.Start() blocks until the connection closes.
  328. func (wsc *wsConnection) OnStart() error {
  329. wsc.BaseService.OnStart()
  330. // these must be set before the readRoutine is created, as it may
  331. // call wsc.Stop(), which accesses these timers
  332. wsc.readTimeout = time.NewTimer(time.Second * wsReadTimeoutSeconds)
  333. wsc.pingTicker = time.NewTicker(time.Second * wsPingTickerSeconds)
  334. // Read subscriptions/unsubscriptions to events
  335. go wsc.readRoutine()
  336. // Custom Ping handler to touch readTimeout
  337. wsc.baseConn.SetPingHandler(func(m string) error {
  338. // NOTE: https://github.com/gorilla/websocket/issues/97
  339. go wsc.baseConn.WriteControl(websocket.PongMessage, []byte(m), time.Now().Add(time.Second*wsWriteTimeoutSeconds))
  340. wsc.readTimeout.Reset(time.Second * wsReadTimeoutSeconds)
  341. return nil
  342. })
  343. wsc.baseConn.SetPongHandler(func(m string) error {
  344. // NOTE: https://github.com/gorilla/websocket/issues/97
  345. wsc.readTimeout.Reset(time.Second * wsReadTimeoutSeconds)
  346. return nil
  347. })
  348. go wsc.readTimeoutRoutine()
  349. // Write responses, BLOCKING.
  350. wsc.writeRoutine()
  351. return nil
  352. }
  353. func (wsc *wsConnection) OnStop() {
  354. wsc.BaseService.OnStop()
  355. if wsc.evsw != nil {
  356. wsc.evsw.RemoveListener(wsc.remoteAddr)
  357. }
  358. wsc.readTimeout.Stop()
  359. wsc.pingTicker.Stop()
  360. // The write loop closes the websocket connection
  361. // when it exits its loop, and the read loop
  362. // closes the writeChan
  363. }
  364. func (wsc *wsConnection) readTimeoutRoutine() {
  365. select {
  366. case <-wsc.readTimeout.C:
  367. wsc.Logger.Info("Stopping connection due to read timeout")
  368. wsc.Stop()
  369. case <-wsc.Quit:
  370. return
  371. }
  372. }
  373. // Implements WSRPCConnection
  374. func (wsc *wsConnection) GetRemoteAddr() string {
  375. return wsc.remoteAddr
  376. }
  377. // Implements WSRPCConnection
  378. func (wsc *wsConnection) GetEventSwitch() events.EventSwitch {
  379. return wsc.evsw
  380. }
  381. // Implements WSRPCConnection
  382. // Blocking write to writeChan until service stops.
  383. // Goroutine-safe
  384. func (wsc *wsConnection) WriteRPCResponse(resp types.RPCResponse) {
  385. select {
  386. case <-wsc.Quit:
  387. return
  388. case wsc.writeChan <- resp:
  389. }
  390. }
  391. // Implements WSRPCConnection
  392. // Nonblocking write.
  393. // Goroutine-safe
  394. func (wsc *wsConnection) TryWriteRPCResponse(resp types.RPCResponse) bool {
  395. select {
  396. case <-wsc.Quit:
  397. return false
  398. case wsc.writeChan <- resp:
  399. return true
  400. default:
  401. return false
  402. }
  403. }
  404. // Read from the socket and subscribe to or unsubscribe from events
  405. func (wsc *wsConnection) readRoutine() {
  406. // Do not close writeChan, to allow WriteRPCResponse() to fail.
  407. // defer close(wsc.writeChan)
  408. for {
  409. select {
  410. case <-wsc.Quit:
  411. return
  412. default:
  413. var in []byte
  414. // Do not set a deadline here like below:
  415. // wsc.baseConn.SetReadDeadline(time.Now().Add(time.Second * wsReadTimeoutSeconds))
  416. // The client may not send anything for a while.
  417. // We use `readTimeout` to handle read timeouts.
  418. _, in, err := wsc.baseConn.ReadMessage()
  419. if err != nil {
  420. wsc.Logger.Info("Failed to read from connection", "remote", wsc.remoteAddr, "err", err.Error())
  421. // an error reading the connection,
  422. // kill the connection
  423. wsc.Stop()
  424. return
  425. }
  426. var request types.RPCRequest
  427. err = json.Unmarshal(in, &request)
  428. if err != nil {
  429. errStr := fmt.Sprintf("Error unmarshaling data: %s", err.Error())
  430. wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, errStr))
  431. continue
  432. }
  433. // Now, fetch the RPCFunc and execute it.
  434. rpcFunc := wsc.funcMap[request.Method]
  435. if rpcFunc == nil {
  436. wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, "RPC method unknown: "+request.Method))
  437. continue
  438. }
  439. var args []reflect.Value
  440. if rpcFunc.ws {
  441. wsCtx := types.WSRPCContext{Request: request, WSRPCConnection: wsc}
  442. args, err = jsonParamsToArgsWS(rpcFunc, request.Params, wsCtx)
  443. } else {
  444. args, err = jsonParamsToArgsRPC(rpcFunc, request.Params)
  445. }
  446. if err != nil {
  447. wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, err.Error()))
  448. continue
  449. }
  450. returns := rpcFunc.f.Call(args)
  451. // TODO: Need to encode args/returns to string if we want to log them
  452. wsc.Logger.Info("WSJSONRPC", "method", request.Method)
  453. result, err := unreflectResult(returns)
  454. if err != nil {
  455. wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, err.Error()))
  456. continue
  457. } else {
  458. wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, result, ""))
  459. continue
  460. }
  461. }
  462. }
  463. }
  464. // receives on a write channel and writes out on the socket
  465. func (wsc *wsConnection) writeRoutine() {
  466. defer wsc.baseConn.Close()
  467. for {
  468. select {
  469. case <-wsc.Quit:
  470. return
  471. case <-wsc.pingTicker.C:
  472. err := wsc.writeMessageWithDeadline(websocket.PingMessage, []byte{})
  473. if err != nil {
  474. wsc.Logger.Error("Failed to write ping message on websocket", "err", err)
  475. wsc.Stop()
  476. return
  477. }
  478. case msg := <-wsc.writeChan:
  479. jsonBytes, err := json.MarshalIndent(msg, "", " ")
  480. if err != nil {
  481. wsc.Logger.Error("Failed to marshal RPCResponse to JSON", "err", err)
  482. } else {
  483. if err = wsc.writeMessageWithDeadline(websocket.TextMessage, jsonBytes); err != nil {
  484. wsc.Logger.Error("Failed to write response on websocket", "err", err)
  485. wsc.Stop()
  486. return
  487. }
  488. }
  489. }
  490. }
  491. }
  492. // All writes to the websocket must (re)set the write deadline.
  493. // If some writes don't set it while others do, they may timeout incorrectly (https://github.com/tendermint/tendermint/issues/553)
  494. func (wsc *wsConnection) writeMessageWithDeadline(msgType int, msg []byte) error {
  495. wsc.baseConn.SetWriteDeadline(time.Now().Add(time.Second * wsWriteTimeoutSeconds))
  496. return wsc.baseConn.WriteMessage(msgType, msg)
  497. }
  498. //----------------------------------------
  499. // Main manager for all websocket connections
  500. // Holds the event switch
  501. // NOTE: The websocket path is defined externally, e.g. in node/node.go
  502. type WebsocketManager struct {
  503. websocket.Upgrader
  504. funcMap map[string]*RPCFunc
  505. evsw events.EventSwitch
  506. logger log.Logger
  507. }
  508. func NewWebsocketManager(funcMap map[string]*RPCFunc, evsw events.EventSwitch) *WebsocketManager {
  509. return &WebsocketManager{
  510. funcMap: funcMap,
  511. evsw: evsw,
  512. Upgrader: websocket.Upgrader{
  513. ReadBufferSize: 1024,
  514. WriteBufferSize: 1024,
  515. CheckOrigin: func(r *http.Request) bool {
  516. // TODO
  517. return true
  518. },
  519. },
  520. logger: log.NewNopLogger(),
  521. }
  522. }
  523. func (wm *WebsocketManager) SetLogger(l log.Logger) {
  524. wm.logger = l
  525. }
  526. // Upgrade the request/response (via http.Hijack) and starts the wsConnection.
  527. func (wm *WebsocketManager) WebsocketHandler(w http.ResponseWriter, r *http.Request) {
  528. wsConn, err := wm.Upgrade(w, r, nil)
  529. if err != nil {
  530. // TODO - return http error
  531. wm.logger.Error("Failed to upgrade to websocket connection", "err", err)
  532. return
  533. }
  534. // register connection
  535. con := NewWSConnection(wsConn, wm.funcMap, wm.evsw)
  536. con.SetLogger(wm.logger)
  537. wm.logger.Info("New websocket connection", "remote", con.remoteAddr)
  538. con.Start() // Blocking
  539. }
  540. // rpc.websocket
  541. //-----------------------------------------------------------------------------
  542. // NOTE: assume returns is result struct and error. If error is not nil, return it
  543. func unreflectResult(returns []reflect.Value) (interface{}, error) {
  544. errV := returns[1]
  545. if errV.Interface() != nil {
  546. return nil, errors.Errorf("%v", errV.Interface())
  547. }
  548. rv := returns[0]
  549. // the result is a registered interface,
  550. // we need a pointer to it so we can marshal with type byte
  551. rvp := reflect.New(rv.Type())
  552. rvp.Elem().Set(rv)
  553. return rvp.Interface(), nil
  554. }
  555. // writes a list of available rpc endpoints as an html page
  556. func writeListOfEndpoints(w http.ResponseWriter, r *http.Request, funcMap map[string]*RPCFunc) {
  557. noArgNames := []string{}
  558. argNames := []string{}
  559. for name, funcData := range funcMap {
  560. if len(funcData.args) == 0 {
  561. noArgNames = append(noArgNames, name)
  562. } else {
  563. argNames = append(argNames, name)
  564. }
  565. }
  566. sort.Strings(noArgNames)
  567. sort.Strings(argNames)
  568. buf := new(bytes.Buffer)
  569. buf.WriteString("<html><body>")
  570. buf.WriteString("<br>Available endpoints:<br>")
  571. for _, name := range noArgNames {
  572. link := fmt.Sprintf("http://%s/%s", r.Host, name)
  573. buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link))
  574. }
  575. buf.WriteString("<br>Endpoints that require arguments:<br>")
  576. for _, name := range argNames {
  577. link := fmt.Sprintf("http://%s/%s?", r.Host, name)
  578. funcData := funcMap[name]
  579. for i, argName := range funcData.argNames {
  580. link += argName + "=_"
  581. if i < len(funcData.argNames)-1 {
  582. link += "&"
  583. }
  584. }
  585. buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link))
  586. }
  587. buf.WriteString("</body></html>")
  588. w.Header().Set("Content-Type", "text/html")
  589. w.WriteHeader(200)
  590. w.Write(buf.Bytes())
  591. }