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 }