github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/network/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/neatio-net/neatio/chain/log" 35 "github.com/rs/cors" 36 ) 37 38 const ( 39 maxRequestContentLength = 1024 * 512 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 220 // Make sure timeout values are meaningful 221 if timeouts.ReadTimeout < time.Second { 222 log.Warn("Sanitizing invalid HTTP read timeout", "provided", timeouts.ReadTimeout, "updated", DefaultHTTPTimeouts.ReadTimeout) 223 timeouts.ReadTimeout = DefaultHTTPTimeouts.ReadTimeout 224 } 225 if timeouts.WriteTimeout < time.Second { 226 log.Warn("Sanitizing invalid HTTP write timeout", "provided", timeouts.WriteTimeout, "updated", DefaultHTTPTimeouts.WriteTimeout) 227 timeouts.WriteTimeout = DefaultHTTPTimeouts.WriteTimeout 228 } 229 if timeouts.IdleTimeout < time.Second { 230 log.Warn("Sanitizing invalid HTTP idle timeout", "provided", timeouts.IdleTimeout, "updated", DefaultHTTPTimeouts.IdleTimeout) 231 timeouts.IdleTimeout = DefaultHTTPTimeouts.IdleTimeout 232 } 233 // Bundle and start the HTTP server 234 return &http.Server{ 235 Handler: handler, 236 ReadTimeout: timeouts.ReadTimeout, 237 WriteTimeout: timeouts.WriteTimeout, 238 IdleTimeout: timeouts.IdleTimeout, 239 } 240 } 241 242 // ServeHTTP serves JSON-RPC requests over HTTP. 243 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 244 // Permit dumb empty requests for remote health-checks (AWS) 245 if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { 246 return 247 } 248 if code, err := validateRequest(r); err != nil { 249 http.Error(w, err.Error(), code) 250 return 251 } 252 // All checks passed, create a codec that reads direct from the request body 253 // untilEOF and writes the response to w and order the server to process a 254 // single request. 255 ctx := r.Context() 256 ctx = context.WithValue(ctx, "remote", r.RemoteAddr) 257 ctx = context.WithValue(ctx, "scheme", r.Proto) 258 ctx = context.WithValue(ctx, "local", r.Host) 259 if ua := r.Header.Get("User-Agent"); ua != "" { 260 ctx = context.WithValue(ctx, "User-Agent", ua) 261 } 262 if origin := r.Header.Get("Origin"); origin != "" { 263 ctx = context.WithValue(ctx, "Origin", origin) 264 } 265 266 w.Header().Set("content-type", contentType) 267 codec := newHTTPServerConn(r, w) 268 defer codec.Close() 269 s.serveSingleRequest(ctx, codec) 270 } 271 272 // validateRequest returns a non-zero response code and error message if the 273 // request is invalid. 274 func validateRequest(r *http.Request) (int, error) { 275 if r.Method == http.MethodPut || r.Method == http.MethodDelete { 276 return http.StatusMethodNotAllowed, errors.New("method not allowed") 277 } 278 if r.ContentLength > maxRequestContentLength { 279 err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength) 280 return http.StatusRequestEntityTooLarge, err 281 } 282 // Allow OPTIONS (regardless of content-type) 283 if r.Method == http.MethodOptions { 284 return 0, nil 285 } 286 // Check content-type 287 if mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")); err == nil { 288 for _, accepted := range acceptedContentTypes { 289 if accepted == mt { 290 return 0, nil 291 } 292 } 293 } 294 // Invalid content-type 295 err := fmt.Errorf("invalid content type, only %s is supported", contentType) 296 return http.StatusUnsupportedMediaType, err 297 } 298 299 func newCorsHandler(srv http.Handler, allowedOrigins []string) http.Handler { 300 // disable CORS support if user has not specified a custom CORS configuration 301 if len(allowedOrigins) == 0 { 302 return srv 303 } 304 c := cors.New(cors.Options{ 305 AllowedOrigins: allowedOrigins, 306 AllowedMethods: []string{http.MethodPost, http.MethodGet}, 307 MaxAge: 600, 308 AllowedHeaders: []string{"*"}, 309 }) 310 return c.Handler(srv) 311 } 312 313 // virtualHostHandler is a handler which validates the Host-header of incoming requests. 314 // The virtualHostHandler can prevent DNS rebinding attacks, which do not utilize CORS-headers, 315 // since they do in-domain requests against the RPC api. Instead, we can see on the Host-header 316 // which domain was used, and validate that against a whitelist. 317 type virtualHostHandler struct { 318 vhosts map[string]struct{} 319 next http.Handler 320 } 321 322 // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler 323 func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 324 // if r.Host is not set, we can continue serving since a browser would set the Host header 325 if r.Host == "" { 326 h.next.ServeHTTP(w, r) 327 return 328 } 329 host, _, err := net.SplitHostPort(r.Host) 330 if err != nil { 331 // Either invalid (too many colons) or no port specified 332 host = r.Host 333 } 334 if ipAddr := net.ParseIP(host); ipAddr != nil { 335 // It's an IP address, we can serve that 336 h.next.ServeHTTP(w, r) 337 return 338 339 } 340 // Not an ip address, but a hostname. Need to validate 341 if _, exist := h.vhosts["*"]; exist { 342 h.next.ServeHTTP(w, r) 343 return 344 } 345 if _, exist := h.vhosts[host]; exist { 346 h.next.ServeHTTP(w, r) 347 return 348 } 349 http.Error(w, "invalid host specified", http.StatusForbidden) 350 } 351 352 func newVHostHandler(vhosts []string, next http.Handler) http.Handler { 353 vhostMap := make(map[string]struct{}) 354 for _, allowedHost := range vhosts { 355 vhostMap[strings.ToLower(allowedHost)] = struct{}{} 356 } 357 return &virtualHostHandler{vhostMap, next} 358 }