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