github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/litrpc/lndcproxy.go (about)

     1  package litrpc
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"github.com/mit-dci/lit/crypto/koblitz"
     9  	"github.com/mit-dci/lit/logging"
    10  	"golang.org/x/net/websocket"
    11  )
    12  
    13  // LndcRpcWebsocketProxy is a regular websocket server that translates the
    14  // received requests into a call on the new LNDC based remote control transport
    15  type LndcRpcWebsocketProxy struct {
    16  	lndcRpcClient *LndcRpcClient
    17  }
    18  
    19  // NewLocalLndcRpcWebsocketProxy is an overload to NewLndcRpcWebsocketProxyWithLndc
    20  // connecting to the local lit using a derived key from the locally stored
    21  // privkey.hex using the default home dir and port
    22  func NewLocalLndcRpcWebsocketProxy() (*LndcRpcWebsocketProxy, error) {
    23  	client, err := NewLocalLndcRpcClient()
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	return NewLndcRpcWebsocketProxyWithLndc(client), nil
    28  }
    29  
    30  // NewLocalLndcRpcWebsocketProxy is an overload to NewLndcRpcWebsocketProxyWithLndc
    31  // connecting to the local lit using a derived key from the locally stored
    32  // privkey.hex using the default home dir and the given port
    33  func NewLocalLndcRpcWebsocketProxyWithPort(port uint32) (*LndcRpcWebsocketProxy, error) {
    34  	client, err := NewLocalLndcRpcClientWithPort(port)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	return NewLndcRpcWebsocketProxyWithLndc(client), nil
    39  }
    40  
    41  // NewLocalLndcRpcWebsocketProxy is an overload to NewLndcRpcWebsocketProxyWithLndc
    42  // connecting to the local lit using a derived key from the locally stored
    43  // privkey.hex using the given homedir and the given port
    44  func NewLocalLndcRpcWebsocketProxyWithHomeDirAndPort(litHomeDir string, port uint32) (*LndcRpcWebsocketProxy, error) {
    45  	client, err := NewLocalLndcRpcClientWithHomeDirAndPort(litHomeDir, port)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	return NewLndcRpcWebsocketProxyWithLndc(client), nil
    50  }
    51  
    52  // NewLndcRpcWebsocketProxy is an overload to NewLndcRpcWebsocketProxyWithLndc
    53  // connecting to the given lit node specified by litAdr, identifying with the
    54  // given key.
    55  func NewLndcRpcWebsocketProxy(litAdr string, key *koblitz.PrivateKey) (*LndcRpcWebsocketProxy, error) {
    56  	client, err := NewLndcRpcClient(litAdr, key)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	return NewLndcRpcWebsocketProxyWithLndc(client), nil
    61  }
    62  
    63  // NewLndcRpcWebsocketProxyWithLndc creates a new proxy that listens on the
    64  // websocket port and translates requests into remote control messages over
    65  // lndc transport.
    66  func NewLndcRpcWebsocketProxyWithLndc(lndcRpcClient *LndcRpcClient) *LndcRpcWebsocketProxy {
    67  	proxy := new(LndcRpcWebsocketProxy)
    68  	proxy.lndcRpcClient = lndcRpcClient
    69  	return proxy
    70  }
    71  
    72  // Listen starts listening on the given host and port for websocket requests
    73  // This function blocks unless an error occurs, so you should call it as a
    74  // goroutine
    75  func (p *LndcRpcWebsocketProxy) Listen(host string, port uint16) {
    76  
    77  	listenString := fmt.Sprintf("%s:%d", host, port)
    78  	http.HandleFunc("/ws",
    79  		func(w http.ResponseWriter, req *http.Request) {
    80  			s := websocket.Server{Handler: websocket.Handler(p.proxyServeWS)}
    81  			s.ServeHTTP(w, req)
    82  		})
    83  	/*http.HandleFunc("/static/", WebUIHandler)
    84  	http.HandleFunc("/", WebUIHandler)
    85  	http.HandleFunc("/oneoff", serveOneoffs)*/
    86  
    87  	logging.Infof("Listening regular Websocket RPC on %s...", listenString)
    88  	err := http.ListenAndServe(listenString, nil)
    89  	logging.Errorf("Error on websocket server: %s", err.Error())
    90  }
    91  
    92  // proxyServeWS handles incoming websocket requests
    93  func (p *LndcRpcWebsocketProxy) proxyServeWS(ws *websocket.Conn) {
    94  
    95  	// Close the connection when we're done
    96  	defer ws.Close()
    97  	for {
    98  
    99  		// Receive a websocket frame
   100  		var data interface{}
   101  		err := websocket.JSON.Receive(ws, &data)
   102  		if err != nil {
   103  			logging.Warnf("Error receiving websocket frame: %s\n", err.Error())
   104  			break
   105  		}
   106  		if data == nil {
   107  			logging.Warnf("Received nil websocket frame")
   108  			break
   109  		}
   110  		var reply interface{}
   111  		// Parse the incoming request as generic JSON
   112  		msg := data.(map[string]interface{})
   113  		method := msg["method"]
   114  		var args interface{}
   115  
   116  		// Default to a NoArgs in case the params array is nil
   117  		args = new(NoArgs)
   118  
   119  		if msg["params"] != nil {
   120  
   121  			// Parse the params into the args object
   122  			argsArray, ok := msg["params"].([]interface{})
   123  			if ok && len(argsArray) > 0 {
   124  				// In JsonRpc 2.0, the params are always an array
   125  				// but lit always only expects a single Args object
   126  				// so we take index 0 from the array.
   127  				args = msg["params"].([]interface{})[0]
   128  			}
   129  		}
   130  
   131  		// store the id from the jsonrpc request
   132  		id := msg["id"]
   133  
   134  		// Use the LNDC RPC client to execute the call
   135  		err = p.lndcRpcClient.Call(method.(string), args, &reply)
   136  		var jsonResponse []byte
   137  		if err != nil {
   138  			// In case of an error send back a JSON RPC formatted error
   139  			// response
   140  			jsonResponse, _ = json.Marshal(errorResponse(id, err))
   141  		} else {
   142  			// In case of success, send back a JSON RPC formatted
   143  			// response
   144  			jsonResponse, _ = json.Marshal(successResponse(id, reply))
   145  		}
   146  
   147  		// Write the response to the stream, and continue with the next websocket
   148  		// frame
   149  		ws.Write(jsonResponse)
   150  	}
   151  
   152  }
   153  
   154  // baseResponse adds the basic elements for each response type to the JSON Response
   155  func baseResponse(requestId interface{}) map[string]interface{} {
   156  	response := make(map[string]interface{})
   157  	response["jsonrpc"] = "2.0"
   158  	response["id"] = requestId
   159  	return response
   160  }
   161  
   162  // errorResponse translates an error into a properly formatted JSON RPC response
   163  func errorResponse(requestId interface{}, err error) map[string]interface{} {
   164  	errorObj := make(map[string]interface{})
   165  	errorObj["code"] = -32000
   166  	errorObj["message"] = "Internal Server Error"
   167  	errorObj["data"] = err.Error()
   168  
   169  	response := baseResponse(requestId)
   170  	response["error"] = errorObj
   171  
   172  	return response
   173  }
   174  
   175  // successResponse formats a reply object into a properly formatted JSON RPC response
   176  func successResponse(requestId interface{}, data interface{}) map[string]interface{} {
   177  	response := baseResponse(requestId)
   178  	response["result"] = data
   179  	return response
   180  }