go-eth-proxy

Transparent proxy server for eth-cache
Info | Log | Files | Refs

rpc.go (3820B)


      1 package rpc
      2 
      3 
      4 import (
      5 	"bytes"
      6 	"encoding/json"
      7 	"fmt"
      8 	"io"
      9 	"log"
     10 	"net/http"
     11 	"net/url"
     12 	"strconv"
     13 	"strings"
     14 )
     15 
     16 type jsonRpcMsg struct {
     17 	Method string
     18 }
     19 
     20 type jsonRpcMsgFull struct {
     21 	Method string
     22 	Id any
     23 	Params []any
     24 }
     25 
     26 type JsonRpcError struct {
     27 	Code int	`json:"code"`
     28 	Message string	`json:"message"`
     29 }
     30 
     31 type JsonRpcResponse struct {
     32 	Id any			`json:"id"`
     33 	Error JsonRpcError	`json:"error"`
     34 }
     35 
     36 type jsonRpcResponseFull struct {
     37 	Jsonrpc string	`json:"jsonrpc"`
     38 	Id any		`json:"id"`
     39 	Result any	`json:"result"`
     40 }
     41 
     42 type ProxyServer struct {
     43 	Server http.Handler
     44 	uri *url.URL
     45 }
     46 
     47 type proxyWriter struct {
     48 	Status int
     49 	header map[string][]string
     50 	data *bytes.Buffer
     51 	afterHeader bool
     52 }
     53 
     54 
     55 func (p *proxyWriter) Header() http.Header {
     56 	return p.header
     57 }
     58 
     59 func (p *proxyWriter) Write(b []byte) (int, error) {
     60 	log.Printf("proxyserver %s", b)
     61 	return p.data.Write(b)
     62 }
     63 
     64 func (p *proxyWriter) WriteHeader(status int) {
     65 	p.Status = status
     66 	p.header["Status"] = []string{fmt.Sprintf("%d", status)}
     67 }
     68 
     69 func (p *proxyWriter) Copy(w http.ResponseWriter) (int, error) {
     70 	c := 0
     71 	l := p.data.Len()
     72 	b := p.data.Bytes()
     73 	for ;c < l; {
     74 		r, err := w.Write(b[c:])
     75 		if err != nil {
     76 			return 0, err
     77 		}
     78 		c += r
     79 	}
     80 	return c, nil
     81 }
     82 
     83 func newProxyWriter() *proxyWriter {
     84 	b := make([]byte, 0, 1024)
     85 	p := &proxyWriter{
     86 		header: make(map[string][]string),
     87 		data: bytes.NewBuffer(b),
     88 	}
     89 	return p
     90 }
     91 
     92 func NewProxyServer(backend http.Handler, remoteURI string) (*ProxyServer, error) {
     93 	var uri *url.URL
     94 	var err error
     95 
     96 	if remoteURI != "" {
     97 		uri, err = url.Parse(remoteURI)
     98 		if err != nil {
     99 			return nil, err
    100 		}
    101 	}
    102 	srv := &ProxyServer{
    103 		Server: backend,
    104 		uri: uri,
    105 	}
    106 	log.Printf("proxy server shadowing: %s", uri)
    107 	return srv, nil
    108 }
    109 
    110 func (s *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    111 	msg := jsonRpcMsg{}
    112 	b := make([]byte, r.ContentLength)
    113 	c, err := io.ReadFull(r.Body, b)
    114 	if (err != nil) {
    115 		log.Printf("%s", err)
    116 		r.Body.Close()
    117 		w.WriteHeader(http.StatusInternalServerError)
    118 		return
    119 	}
    120 	r.Body.Close()
    121 	err = json.Unmarshal(b, &msg)
    122 	if (err != nil) {
    123 		log.Printf("%s", err)
    124 		r.Body.Close()
    125 		w.WriteHeader(http.StatusInternalServerError)
    126 		return
    127 	}
    128 
    129 	rr := bytes.NewReader(b)
    130 	r.Body = io.NopCloser(rr)
    131 
    132 	for _, k := range([]string{
    133 		"eth_getTransactionByHash",
    134 		"eth_getTransactionReceipt",
    135 		"eth_getBlockByNumber",
    136 		"eth_getBlockByHash",
    137 	}) {
    138 		rw := newProxyWriter()
    139 		if msg.Method == k {
    140 			log.Printf("proxy match method %s %s", k, msg.Method)
    141 			s.Server.ServeHTTP(rw, r)
    142 			hd := rw.Header()
    143 			parts := strings.SplitN(hd["Status"][0], " ", 1)
    144 			status, err := strconv.ParseInt(parts[0], 10, 16)
    145 			if (err != nil) {
    146 				r.Body.Close()
    147 				w.WriteHeader(http.StatusInternalServerError)
    148 				return
    149 			}
    150 			if status < 300 && status >= 200 {
    151 				rsp := JsonRpcResponse{
    152 					Error: JsonRpcError{},
    153 				}
    154 				err = json.Unmarshal(b, &rsp)
    155 				if (err != nil) {
    156 					r.Body.Close()
    157 					w.WriteHeader(http.StatusInternalServerError)
    158 					return
    159 				}
    160 				if rsp.Error.Code == 0 {
    161 					rw.WriteHeader(http.StatusOK)
    162 					rw.Copy(w)
    163 					return
    164 				}
    165 			}
    166 
    167 			log.Printf("not found in proxy: %s", k)
    168 			rr.Seek(0, io.SeekStart)
    169 		}
    170 	}
    171 
    172 	if s.uri == nil {
    173 		log.Printf("missing remote side for unproxied method: %s", msg.Method)
    174 		w.WriteHeader(http.StatusBadGateway)
    175 		return
    176 	}
    177 
    178 	client_req := &http.Request{}
    179 	client_req.Method = "POST"
    180 	client_req.URL = s.uri
    181 	client_req.Body = r.Body
    182 	client_req.ContentLength = int64(c)
    183 	client := &http.Client{}
    184 	res, err := client.Do(client_req)
    185 	if err != nil {
    186 		log.Printf("%s", err)
    187 		r.Body.Close()
    188 		w.WriteHeader(http.StatusBadGateway)
    189 		return
    190 	}
    191 	if res.StatusCode != http.StatusOK {
    192 		v, _ := io.ReadAll(res.Body)
    193 		log.Printf("%s", v)
    194 	}
    195 	w.WriteHeader(res.StatusCode)
    196 	rrr := io.TeeReader(res.Body, w)
    197 	io.ReadAll(rrr)
    198 }
    199