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