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

     1  package litrpc
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"net/rpc"
    10  	"net/rpc/jsonrpc"
    11  	"strings"
    12  
    13  	"github.com/mit-dci/lit/logging"
    14  
    15  	"golang.org/x/net/websocket"
    16  
    17  	"github.com/mit-dci/lit/qln"
    18  	"github.com/mit-dci/lit/webui"
    19  )
    20  
    21  /*
    22  Remote Procedure Calls
    23  RPCs are how people tell the lit node what to do.
    24  It ends up being the root of ~everything in the executable.
    25  
    26  */
    27  
    28  // A LitRPC is the user I/O interface; it owns and initialized a SPVCon and LitNode
    29  // and listens and responds on RPC
    30  
    31  type LitRPC struct {
    32  	Node      *qln.LitNode
    33  	OffButton chan bool
    34  }
    35  
    36  func serveWS(ws *websocket.Conn) {
    37  	body, err := ioutil.ReadAll(ws.Request().Body)
    38  	if err != nil {
    39  		logging.Errorf("Error reading body: %v", err)
    40  		return
    41  	}
    42  
    43  	logging.Infof(string(body))
    44  	ws.Request().Body = ioutil.NopCloser(bytes.NewBuffer(body))
    45  
    46  	jsonrpc.ServeConn(ws)
    47  }
    48  
    49  func WebUIHandler(w http.ResponseWriter, r *http.Request) {
    50  	if r.URL.Path == "/" {
    51  		r.URL.Path = "/index.html"
    52  	}
    53  
    54  	bytes, err := webui.Asset(r.URL.Path[1:])
    55  	if err != nil {
    56  		logging.Errorf("Error serving request [%s]: [%s]\n", r.URL.Path, err.Error())
    57  		w.WriteHeader(404)
    58  		return
    59  	}
    60  
    61  	if strings.HasSuffix(r.URL.Path, ".html") {
    62  		w.Header().Add("Content-Type", "text/html")
    63  	} else if strings.HasSuffix(r.URL.Path, ".ico") {
    64  		w.Header().Add("Content-Type", "image/x-icon")
    65  	} else if strings.HasSuffix(r.URL.Path, ".js") {
    66  		w.Header().Add("Content-Type", "application/javascript")
    67  	} else if strings.HasSuffix(r.URL.Path, ".json") {
    68  		w.Header().Add("Content-Type", "application/json")
    69  	} else if strings.HasSuffix(r.URL.Path, ".css") {
    70  		w.Header().Add("Content-Type", "text/css")
    71  	}
    72  
    73  	w.WriteHeader(200)
    74  	w.Write(bytes)
    75  }
    76  
    77  // OiOoReadWriter is for One-In-One-Out Reader/Writer and I hope to god that it works.
    78  type OiOoReadWriter struct {
    79  	from io.ReadCloser
    80  	to   io.Writer
    81  }
    82  
    83  func (o OiOoReadWriter) Read(p []byte) (int, error) {
    84  	return o.from.Read(p)
    85  }
    86  
    87  func (o OiOoReadWriter) Write(p []byte) (int, error) {
    88  	return o.to.Write(p)
    89  }
    90  
    91  func (o OiOoReadWriter) Close() error {
    92  	return o.from.Close()
    93  }
    94  
    95  func serveOneoffs(rw http.ResponseWriter, req *http.Request) {
    96  	o := OiOoReadWriter{
    97  		from: req.Body,
    98  		to:   rw,
    99  	}
   100  	jsonrpc.ServeConn(o)
   101  	o.Close()
   102  }
   103  
   104  func RPCListen(rpcl *LitRPC, host string, port uint16) {
   105  
   106  	rpc.Register(rpcl)
   107  	listenString := fmt.Sprintf("%s:%d", host, port)
   108  
   109  	http.Handle("/ws", websocket.Handler(serveWS))
   110  	http.HandleFunc("/static/", WebUIHandler)
   111  	http.HandleFunc("/", WebUIHandler)
   112  	http.HandleFunc("/oneoff", serveOneoffs)
   113  	logging.Fatal(http.ListenAndServe(listenString, nil))
   114  }