github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/rpc/comms/http.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package comms
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"net"
    23  	"net/http"
    24  	"strings"
    25  	"sync"
    26  	"time"
    27  
    28  	"bytes"
    29  	"io"
    30  	"io/ioutil"
    31  
    32  	"github.com/ethereum/go-ethereum/logger"
    33  	"github.com/ethereum/go-ethereum/logger/glog"
    34  	"github.com/ethereum/go-ethereum/rpc/codec"
    35  	"github.com/ethereum/go-ethereum/rpc/shared"
    36  	"github.com/rs/cors"
    37  )
    38  
    39  const (
    40  	serverIdleTimeout  = 10 * time.Second // idle keep-alive connections
    41  	serverReadTimeout  = 15 * time.Second // per-request read timeout
    42  	serverWriteTimeout = 15 * time.Second // per-request read timeout
    43  )
    44  
    45  var (
    46  	httpServerMu sync.Mutex
    47  	httpServer   *stopServer
    48  )
    49  
    50  type HttpConfig struct {
    51  	ListenAddress string
    52  	ListenPort    uint
    53  	CorsDomain    string
    54  }
    55  
    56  // stopServer augments http.Server with idle connection tracking.
    57  // Idle keep-alive connections are shut down when Close is called.
    58  type stopServer struct {
    59  	*http.Server
    60  	l net.Listener
    61  	// connection tracking state
    62  	mu       sync.Mutex
    63  	shutdown bool // true when Stop has returned
    64  	idle     map[net.Conn]struct{}
    65  }
    66  
    67  type handler struct {
    68  	codec codec.Codec
    69  	api   shared.EthereumApi
    70  }
    71  
    72  // StartHTTP starts listening for RPC requests sent via HTTP.
    73  func StartHttp(cfg HttpConfig, codec codec.Codec, api shared.EthereumApi) error {
    74  	httpServerMu.Lock()
    75  	defer httpServerMu.Unlock()
    76  
    77  	addr := fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort)
    78  	if httpServer != nil {
    79  		if addr != httpServer.Addr {
    80  			return fmt.Errorf("RPC service already running on %s ", httpServer.Addr)
    81  		}
    82  		return nil // RPC service already running on given host/port
    83  	}
    84  	// Set up the request handler, wrapping it with CORS headers if configured.
    85  	handler := http.Handler(&handler{codec, api})
    86  	if len(cfg.CorsDomain) > 0 {
    87  		opts := cors.Options{
    88  			AllowedMethods: []string{"POST"},
    89  			AllowedOrigins: strings.Split(cfg.CorsDomain, " "),
    90  		}
    91  		handler = cors.New(opts).Handler(handler)
    92  	}
    93  	// Start the server.
    94  	s, err := listenHTTP(addr, handler)
    95  	if err != nil {
    96  		glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", cfg.ListenAddress, cfg.ListenPort, err)
    97  		return err
    98  	}
    99  	httpServer = s
   100  	return nil
   101  }
   102  
   103  func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   104  	w.Header().Set("Content-Type", "application/json")
   105  
   106  	// Limit request size to resist DoS
   107  	if req.ContentLength > maxHttpSizeReqLength {
   108  		err := fmt.Errorf("Request too large")
   109  		response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err)
   110  		sendJSON(w, &response)
   111  		return
   112  	}
   113  
   114  	defer req.Body.Close()
   115  	payload, err := ioutil.ReadAll(req.Body)
   116  	if err != nil {
   117  		err := fmt.Errorf("Could not read request body")
   118  		response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err)
   119  		sendJSON(w, &response)
   120  		return
   121  	}
   122  
   123  	c := h.codec.New(nil)
   124  	var rpcReq shared.Request
   125  	if err = c.Decode(payload, &rpcReq); err == nil {
   126  		reply, err := h.api.Execute(&rpcReq)
   127  		res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
   128  		sendJSON(w, &res)
   129  		return
   130  	}
   131  
   132  	var reqBatch []shared.Request
   133  	if err = c.Decode(payload, &reqBatch); err == nil {
   134  		resBatch := make([]*interface{}, len(reqBatch))
   135  		resCount := 0
   136  		for i, rpcReq := range reqBatch {
   137  			reply, err := h.api.Execute(&rpcReq)
   138  			if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal
   139  				resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
   140  				resCount += 1
   141  			}
   142  		}
   143  		// make response omitting nil entries
   144  		sendJSON(w, resBatch[:resCount])
   145  		return
   146  	}
   147  
   148  	// invalid request
   149  	err = fmt.Errorf("Could not decode request")
   150  	res := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32600, err)
   151  	sendJSON(w, res)
   152  }
   153  
   154  func sendJSON(w io.Writer, v interface{}) {
   155  	if glog.V(logger.Detail) {
   156  		if payload, err := json.MarshalIndent(v, "", "\t"); err == nil {
   157  			glog.Infof("Sending payload: %s", payload)
   158  		}
   159  	}
   160  	if err := json.NewEncoder(w).Encode(v); err != nil {
   161  		glog.V(logger.Error).Infoln("Error sending JSON:", err)
   162  	}
   163  }
   164  
   165  // Stop closes all active HTTP connections and shuts down the server.
   166  func StopHttp() {
   167  	httpServerMu.Lock()
   168  	defer httpServerMu.Unlock()
   169  	if httpServer != nil {
   170  		httpServer.Close()
   171  		httpServer = nil
   172  	}
   173  }
   174  
   175  func listenHTTP(addr string, h http.Handler) (*stopServer, error) {
   176  	l, err := net.Listen("tcp", addr)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  	s := &stopServer{l: l, idle: make(map[net.Conn]struct{})}
   181  	s.Server = &http.Server{
   182  		Addr:         addr,
   183  		Handler:      h,
   184  		ReadTimeout:  serverReadTimeout,
   185  		WriteTimeout: serverWriteTimeout,
   186  		ConnState:    s.connState,
   187  	}
   188  	go s.Serve(l)
   189  	return s, nil
   190  }
   191  
   192  func (s *stopServer) connState(c net.Conn, state http.ConnState) {
   193  	s.mu.Lock()
   194  	defer s.mu.Unlock()
   195  	// Close c immediately if we're past shutdown.
   196  	if s.shutdown {
   197  		if state != http.StateClosed {
   198  			c.Close()
   199  		}
   200  		return
   201  	}
   202  	if state == http.StateIdle {
   203  		s.idle[c] = struct{}{}
   204  	} else {
   205  		delete(s.idle, c)
   206  	}
   207  }
   208  
   209  func (s *stopServer) Close() {
   210  	s.mu.Lock()
   211  	defer s.mu.Unlock()
   212  	// Shut down the acceptor. No new connections can be created.
   213  	s.l.Close()
   214  	// Drop all idle connections. Non-idle connections will be
   215  	// closed by connState as soon as they become idle.
   216  	s.shutdown = true
   217  	for c := range s.idle {
   218  		glog.V(logger.Detail).Infof("closing idle connection %v", c.RemoteAddr())
   219  		c.Close()
   220  		delete(s.idle, c)
   221  	}
   222  }
   223  
   224  type httpClient struct {
   225  	address string
   226  	port    uint
   227  	codec   codec.ApiCoder
   228  	lastRes interface{}
   229  	lastErr error
   230  }
   231  
   232  // Create a new in process client
   233  func NewHttpClient(cfg HttpConfig, c codec.Codec) *httpClient {
   234  	return &httpClient{
   235  		address: cfg.ListenAddress,
   236  		port:    cfg.ListenPort,
   237  		codec:   c.New(nil),
   238  	}
   239  }
   240  
   241  func (self *httpClient) Close() {
   242  	// do nothing
   243  }
   244  
   245  func (self *httpClient) Send(req interface{}) error {
   246  	var body []byte
   247  	var err error
   248  
   249  	self.lastRes = nil
   250  	self.lastErr = nil
   251  
   252  	if body, err = self.codec.Encode(req); err != nil {
   253  		return err
   254  	}
   255  
   256  	httpReq, err := http.NewRequest("POST", fmt.Sprintf("%s:%d", self.address, self.port), bytes.NewBuffer(body))
   257  	if err != nil {
   258  		return err
   259  	}
   260  	httpReq.Header.Set("Content-Type", "application/json")
   261  
   262  	client := http.Client{}
   263  	resp, err := client.Do(httpReq)
   264  	if err != nil {
   265  		return err
   266  	}
   267  
   268  	defer resp.Body.Close()
   269  
   270  	if resp.Status == "200 OK" {
   271  		reply, _ := ioutil.ReadAll(resp.Body)
   272  		var rpcSuccessResponse shared.SuccessResponse
   273  		if err = self.codec.Decode(reply, &rpcSuccessResponse); err == nil {
   274  			self.lastRes = rpcSuccessResponse.Result
   275  			self.lastErr = err
   276  			return nil
   277  		} else {
   278  			var rpcErrorResponse shared.ErrorResponse
   279  			if err = self.codec.Decode(reply, &rpcErrorResponse); err == nil {
   280  				self.lastRes = rpcErrorResponse.Error
   281  				self.lastErr = err
   282  				return nil
   283  			} else {
   284  				return err
   285  			}
   286  		}
   287  	}
   288  
   289  	return fmt.Errorf("Not implemented")
   290  }
   291  
   292  func (self *httpClient) Recv() (interface{}, error) {
   293  	return self.lastRes, self.lastErr
   294  }
   295  
   296  func (self *httpClient) SupportedModules() (map[string]string, error) {
   297  	var body []byte
   298  	var err error
   299  
   300  	payload := shared.Request{
   301  		Id:      1,
   302  		Jsonrpc: "2.0",
   303  		Method:  "modules",
   304  	}
   305  
   306  	if body, err = self.codec.Encode(payload); err != nil {
   307  		return nil, err
   308  	}
   309  
   310  	req, err := http.NewRequest("POST", fmt.Sprintf("%s:%d", self.address, self.port), bytes.NewBuffer(body))
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  	req.Header.Set("Content-Type", "application/json")
   315  
   316  	client := http.Client{}
   317  	resp, err := client.Do(req)
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  
   322  	defer resp.Body.Close()
   323  
   324  	if resp.Status == "200 OK" {
   325  		reply, _ := ioutil.ReadAll(resp.Body)
   326  		var rpcRes shared.SuccessResponse
   327  		if err = self.codec.Decode(reply, &rpcRes); err != nil {
   328  			return nil, err
   329  		}
   330  
   331  		result := make(map[string]string)
   332  		if modules, ok := rpcRes.Result.(map[string]interface{}); ok {
   333  			for a, v := range modules {
   334  				result[a] = fmt.Sprintf("%s", v)
   335  			}
   336  			return result, nil
   337  		}
   338  		err = fmt.Errorf("Unable to parse module response - %v", rpcRes.Result)
   339  	} else {
   340  		fmt.Printf("resp.Status = %s\n", resp.Status)
   341  		fmt.Printf("err = %v\n", err)
   342  	}
   343  
   344  	return nil, err
   345  }