github.com/julesgoullee/go-ethereum@v1.9.7/rpc/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 rpc 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "mime" 28 "net" 29 "net/http" 30 "strings" 31 "sync" 32 "time" 33 34 "github.com/ethereum/go-ethereum/log" 35 "github.com/rs/cors" 36 ) 37 38 const ( 39 maxRequestContentLength = 1024 * 1024 * 5 40 contentType = "application/json" 41 ) 42 43 // https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13 44 var acceptedContentTypes = []string{contentType, "application/json-rpc", "application/jsonrequest"} 45 46 type httpConn struct { 47 client *http.Client 48 req *http.Request 49 closeOnce sync.Once 50 closed chan interface{} 51 } 52 53 // httpConn is treated specially by Client. 54 func (hc *httpConn) Write(context.Context, interface{}) error { 55 panic("Write called on httpConn") 56 } 57 58 func (hc *httpConn) RemoteAddr() string { 59 return hc.req.URL.String() 60 } 61 62 func (hc *httpConn) Read() ([]*jsonrpcMessage, bool, error) { 63 <-hc.closed 64 return nil, false, io.EOF 65 } 66 67 func (hc *httpConn) Close() { 68 hc.closeOnce.Do(func() { close(hc.closed) }) 69 } 70 71 func (hc *httpConn) Closed() <-chan interface{} { 72 return hc.closed 73 } 74 75 // HTTPTimeouts represents the configuration params for the HTTP RPC server. 76 type HTTPTimeouts struct { 77 // ReadTimeout is the maximum duration for reading the entire 78 // request, including the body. 79 // 80 // Because ReadTimeout does not let Handlers make per-request 81 // decisions on each request body's acceptable deadline or 82 // upload rate, most users will prefer to use 83 // ReadHeaderTimeout. It is valid to use them both. 84 ReadTimeout time.Duration 85 86 // WriteTimeout is the maximum duration before timing out 87 // writes of the response. It is reset whenever a new 88 // request's header is read. Like ReadTimeout, it does not 89 // let Handlers make decisions on a per-request basis. 90 WriteTimeout time.Duration 91 92 // IdleTimeout is the maximum amount of time to wait for the 93 // next request when keep-alives are enabled. If IdleTimeout 94 // is zero, the value of ReadTimeout is used. If both are 95 // zero, ReadHeaderTimeout is used. 96 IdleTimeout time.Duration 97 } 98 99 // DefaultHTTPTimeouts represents the default timeout values used if further 100 // configuration is not provided. 101 var DefaultHTTPTimeouts = HTTPTimeouts{ 102 ReadTimeout: 30 * time.Second, 103 WriteTimeout: 30 * time.Second, 104 IdleTimeout: 120 * time.Second, 105 } 106 107 // DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP 108 // using the provided HTTP Client. 109 func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { 110 req, err := http.NewRequest(http.MethodPost, endpoint, nil) 111 if err != nil { 112 return nil, err 113 } 114 req.Header.Set("Content-Type", contentType) 115 req.Header.Set("Accept", contentType) 116 117 initctx := context.Background() 118 return newClient(initctx, func(context.Context) (ServerCodec, error) { 119 return &httpConn{client: client, req: req, closed: make(chan interface{})}, nil 120 }) 121 } 122 123 // DialHTTP creates a new RPC client that connects to an RPC server over HTTP. 124 func DialHTTP(endpoint string) (*Client, error) { 125 return DialHTTPWithClient(endpoint, new(http.Client)) 126 } 127 128 func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { 129 hc := c.writeConn.(*httpConn) 130 respBody, err := hc.doRequest(ctx, msg) 131 if respBody != nil { 132 defer respBody.Close() 133 } 134 135 if err != nil { 136 if respBody != nil { 137 buf := new(bytes.Buffer) 138 if _, err2 := buf.ReadFrom(respBody); err2 == nil { 139 return fmt.Errorf("%v %v", err, buf.String()) 140 } 141 } 142 return err 143 } 144 var respmsg jsonrpcMessage 145 if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil { 146 return err 147 } 148 op.resp <- &respmsg 149 return nil 150 } 151 152 func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonrpcMessage) error { 153 hc := c.writeConn.(*httpConn) 154 respBody, err := hc.doRequest(ctx, msgs) 155 if err != nil { 156 return err 157 } 158 defer respBody.Close() 159 var respmsgs []jsonrpcMessage 160 if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { 161 return err 162 } 163 for i := 0; i < len(respmsgs); i++ { 164 op.resp <- &respmsgs[i] 165 } 166 return nil 167 } 168 169 func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadCloser, error) { 170 body, err := json.Marshal(msg) 171 if err != nil { 172 return nil, err 173 } 174 req := hc.req.WithContext(ctx) 175 req.Body = ioutil.NopCloser(bytes.NewReader(body)) 176 req.ContentLength = int64(len(body)) 177 178 resp, err := hc.client.Do(req) 179 if err != nil { 180 return nil, err 181 } 182 if resp.StatusCode < 200 || resp.StatusCode >= 300 { 183 return resp.Body, errors.New(resp.Status) 184 } 185 return resp.Body, nil 186 } 187 188 // httpServerConn turns a HTTP connection into a Conn. 189 type httpServerConn struct { 190 io.Reader 191 io.Writer 192 r *http.Request 193 } 194 195 func newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec { 196 body := io.LimitReader(r.Body, maxRequestContentLength) 197 conn := &httpServerConn{Reader: body, Writer: w, r: r} 198 return NewJSONCodec(conn) 199 } 200 201 // Close does nothing and always returns nil. 202 func (t *httpServerConn) Close() error { return nil } 203 204 // RemoteAddr returns the peer address of the underlying connection. 205 func (t *httpServerConn) RemoteAddr() string { 206 return t.r.RemoteAddr 207 } 208 209 // SetWriteDeadline does nothing and always returns nil. 210 func (t *httpServerConn) SetWriteDeadline(time.Time) error { return nil } 211 212 // NewHTTPServer creates a new HTTP RPC server around an API provider. 213 // 214 // Deprecated: Server implements http.Handler 215 func NewHTTPServer(cors []string, vhosts []string, timeouts HTTPTimeouts, srv http.Handler) *http.Server { 216 // Wrap the CORS-handler within a host-handler 217 handler := newCorsHandler(srv, cors) 218 handler = newVHostHandler(vhosts, handler) 219 handler = newGzipHandler(handler) 220 221 // Make sure timeout values are meaningful 222 if timeouts.ReadTimeout < time.Second { 223 log.Warn("Sanitizing invalid HTTP read timeout", "provided", timeouts.ReadTimeout, "updated", DefaultHTTPTimeouts.ReadTimeout) 224 timeouts.ReadTimeout = DefaultHTTPTimeouts.ReadTimeout 225 } 226 if timeouts.WriteTimeout < time.Second { 227 log.Warn("Sanitizing invalid HTTP write timeout", "provided", timeouts.WriteTimeout, "updated", DefaultHTTPTimeouts.WriteTimeout) 228 timeouts.WriteTimeout = DefaultHTTPTimeouts.WriteTimeout 229 } 230 if timeouts.IdleTimeout < time.Second { 231 log.Warn("Sanitizing invalid HTTP idle timeout", "provided", timeouts.IdleTimeout, "updated", DefaultHTTPTimeouts.IdleTimeout) 232 timeouts.IdleTimeout = DefaultHTTPTimeouts.IdleTimeout 233 } 234 // Bundle and start the HTTP server 235 return &http.Server{ 236 Handler: handler, 237 ReadTimeout: timeouts.ReadTimeout, 238 WriteTimeout: timeouts.WriteTimeout, 239 IdleTimeout: timeouts.IdleTimeout, 240 } 241 } 242 243 // ServeHTTP serves JSON-RPC requests over HTTP. 244 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 245 // Permit dumb empty requests for remote health-checks (AWS) 246 if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { 247 return 248 } 249 if code, err := validateRequest(r); err != nil { 250 http.Error(w, err.Error(), code) 251 return 252 } 253 // All checks passed, create a codec that reads direct from the request body 254 // untilEOF and writes the response to w and order the server to process a 255 // single request. 256 ctx := r.Context() 257 ctx = context.WithValue(ctx, "remote", r.RemoteAddr) 258 ctx = context.WithValue(ctx, "scheme", r.Proto) 259 ctx = context.WithValue(ctx, "local", r.Host) 260 if ua := r.Header.Get("User-Agent"); ua != "" { 261 ctx = context.WithValue(ctx, "User-Agent", ua) 262 } 263 if origin := r.Header.Get("Origin"); origin != "" { 264 ctx = context.WithValue(ctx, "Origin", origin) 265 } 266 267 w.Header().Set("content-type", contentType) 268 codec := newHTTPServerConn(r, w) 269 defer codec.Close() 270 s.serveSingleRequest(ctx, codec) 271 } 272 273 // validateRequest returns a non-zero response code and error message if the 274 // request is invalid. 275 func validateRequest(r *http.Request) (int, error) { 276 if r.Method == http.MethodPut || r.Method == http.MethodDelete { 277 return http.StatusMethodNotAllowed, errors.New("method not allowed") 278 } 279 if r.ContentLength > maxRequestContentLength { 280 err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength) 281 return http.StatusRequestEntityTooLarge, err 282 } 283 // Allow OPTIONS (regardless of content-type) 284 if r.Method == http.MethodOptions { 285 return 0, nil 286 } 287 // Check content-type 288 if mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")); err == nil { 289 for _, accepted := range acceptedContentTypes { 290 if accepted == mt { 291 return 0, nil 292 } 293 } 294 } 295 // Invalid content-type 296 err := fmt.Errorf("invalid content type, only %s is supported", contentType) 297 return http.StatusUnsupportedMediaType, err 298 } 299 300 func newCorsHandler(srv http.Handler, allowedOrigins []string) http.Handler { 301 // disable CORS support if user has not specified a custom CORS configuration 302 if len(allowedOrigins) == 0 { 303 return srv 304 } 305 c := cors.New(cors.Options{ 306 AllowedOrigins: allowedOrigins, 307 AllowedMethods: []string{http.MethodPost, http.MethodGet}, 308 MaxAge: 600, 309 AllowedHeaders: []string{"*"}, 310 }) 311 return c.Handler(srv) 312 } 313 314 // virtualHostHandler is a handler which validates the Host-header of incoming requests. 315 // The virtualHostHandler can prevent DNS rebinding attacks, which do not utilize CORS-headers, 316 // since they do in-domain requests against the RPC api. Instead, we can see on the Host-header 317 // which domain was used, and validate that against a whitelist. 318 type virtualHostHandler struct { 319 vhosts map[string]struct{} 320 next http.Handler 321 } 322 323 // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler 324 func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 325 // if r.Host is not set, we can continue serving since a browser would set the Host header 326 if r.Host == "" { 327 h.next.ServeHTTP(w, r) 328 return 329 } 330 host, _, err := net.SplitHostPort(r.Host) 331 if err != nil { 332 // Either invalid (too many colons) or no port specified 333 host = r.Host 334 } 335 if ipAddr := net.ParseIP(host); ipAddr != nil { 336 // It's an IP address, we can serve that 337 h.next.ServeHTTP(w, r) 338 return 339 340 } 341 // Not an ip address, but a hostname. Need to validate 342 if _, exist := h.vhosts["*"]; exist { 343 h.next.ServeHTTP(w, r) 344 return 345 } 346 if _, exist := h.vhosts[host]; exist { 347 h.next.ServeHTTP(w, r) 348 return 349 } 350 http.Error(w, "invalid host specified", http.StatusForbidden) 351 } 352 353 func newVHostHandler(vhosts []string, next http.Handler) http.Handler { 354 vhostMap := make(map[string]struct{}) 355 for _, allowedHost := range vhosts { 356 vhostMap[strings.ToLower(allowedHost)] = struct{}{} 357 } 358 return &virtualHostHandler{vhostMap, next} 359 }