github.com/jimmyx0x/go-ethereum@v1.10.28/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 "math" 27 "mime" 28 "net/http" 29 "net/url" 30 "strconv" 31 "sync" 32 "time" 33 ) 34 35 const ( 36 maxRequestContentLength = 1024 * 1024 * 5 37 contentType = "application/json" 38 ) 39 40 // https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13 41 var acceptedContentTypes = []string{contentType, "application/json-rpc", "application/jsonrequest"} 42 43 type httpConn struct { 44 client *http.Client 45 url string 46 closeOnce sync.Once 47 closeCh chan interface{} 48 mu sync.Mutex // protects headers 49 headers http.Header 50 auth HTTPAuth 51 } 52 53 // httpConn implements ServerCodec, but it is treated specially by Client 54 // and some methods don't work. The panic() stubs here exist to ensure 55 // this special treatment is correct. 56 57 func (hc *httpConn) writeJSON(context.Context, interface{}, bool) error { 58 panic("writeJSON called on httpConn") 59 } 60 61 func (hc *httpConn) peerInfo() PeerInfo { 62 panic("peerInfo called on httpConn") 63 } 64 65 func (hc *httpConn) remoteAddr() string { 66 return hc.url 67 } 68 69 func (hc *httpConn) readBatch() ([]*jsonrpcMessage, bool, error) { 70 <-hc.closeCh 71 return nil, false, io.EOF 72 } 73 74 func (hc *httpConn) close() { 75 hc.closeOnce.Do(func() { close(hc.closeCh) }) 76 } 77 78 func (hc *httpConn) closed() <-chan interface{} { 79 return hc.closeCh 80 } 81 82 // HTTPTimeouts represents the configuration params for the HTTP RPC server. 83 type HTTPTimeouts struct { 84 // ReadTimeout is the maximum duration for reading the entire 85 // request, including the body. 86 // 87 // Because ReadTimeout does not let Handlers make per-request 88 // decisions on each request body's acceptable deadline or 89 // upload rate, most users will prefer to use 90 // ReadHeaderTimeout. It is valid to use them both. 91 ReadTimeout time.Duration 92 93 // ReadHeaderTimeout is the amount of time allowed to read 94 // request headers. The connection's read deadline is reset 95 // after reading the headers and the Handler can decide what 96 // is considered too slow for the body. If ReadHeaderTimeout 97 // is zero, the value of ReadTimeout is used. If both are 98 // zero, there is no timeout. 99 ReadHeaderTimeout time.Duration 100 101 // WriteTimeout is the maximum duration before timing out 102 // writes of the response. It is reset whenever a new 103 // request's header is read. Like ReadTimeout, it does not 104 // let Handlers make decisions on a per-request basis. 105 WriteTimeout time.Duration 106 107 // IdleTimeout is the maximum amount of time to wait for the 108 // next request when keep-alives are enabled. If IdleTimeout 109 // is zero, the value of ReadTimeout is used. If both are 110 // zero, ReadHeaderTimeout is used. 111 IdleTimeout time.Duration 112 } 113 114 // DefaultHTTPTimeouts represents the default timeout values used if further 115 // configuration is not provided. 116 var DefaultHTTPTimeouts = HTTPTimeouts{ 117 ReadTimeout: 30 * time.Second, 118 ReadHeaderTimeout: 30 * time.Second, 119 WriteTimeout: 30 * time.Second, 120 IdleTimeout: 120 * time.Second, 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 // DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP 129 // using the provided HTTP Client. 130 // 131 // Deprecated: use DialOptions and the WithHTTPClient option. 132 func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { 133 // Sanity check URL so we don't end up with a client that will fail every request. 134 _, err := url.Parse(endpoint) 135 if err != nil { 136 return nil, err 137 } 138 139 var cfg clientConfig 140 fn := newClientTransportHTTP(endpoint, &cfg) 141 return newClient(context.Background(), fn) 142 } 143 144 func newClientTransportHTTP(endpoint string, cfg *clientConfig) reconnectFunc { 145 headers := make(http.Header, 2+len(cfg.httpHeaders)) 146 headers.Set("accept", contentType) 147 headers.Set("content-type", contentType) 148 for key, values := range cfg.httpHeaders { 149 headers[key] = values 150 } 151 152 client := cfg.httpClient 153 if client == nil { 154 client = new(http.Client) 155 } 156 157 hc := &httpConn{ 158 client: client, 159 headers: headers, 160 url: endpoint, 161 auth: cfg.httpAuth, 162 closeCh: make(chan interface{}), 163 } 164 165 return func(ctx context.Context) (ServerCodec, error) { 166 return hc, nil 167 } 168 } 169 170 func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { 171 hc := c.writeConn.(*httpConn) 172 respBody, err := hc.doRequest(ctx, msg) 173 if err != nil { 174 return err 175 } 176 defer respBody.Close() 177 178 var respmsg jsonrpcMessage 179 if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil { 180 return err 181 } 182 op.resp <- &respmsg 183 return nil 184 } 185 186 func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonrpcMessage) error { 187 hc := c.writeConn.(*httpConn) 188 respBody, err := hc.doRequest(ctx, msgs) 189 if err != nil { 190 return err 191 } 192 defer respBody.Close() 193 var respmsgs []jsonrpcMessage 194 if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { 195 return err 196 } 197 if len(respmsgs) != len(msgs) { 198 return fmt.Errorf("batch has %d requests but response has %d: %w", len(msgs), len(respmsgs), ErrBadResult) 199 } 200 for i := 0; i < len(respmsgs); i++ { 201 op.resp <- &respmsgs[i] 202 } 203 return nil 204 } 205 206 func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadCloser, error) { 207 body, err := json.Marshal(msg) 208 if err != nil { 209 return nil, err 210 } 211 req, err := http.NewRequestWithContext(ctx, "POST", hc.url, io.NopCloser(bytes.NewReader(body))) 212 if err != nil { 213 return nil, err 214 } 215 req.ContentLength = int64(len(body)) 216 req.GetBody = func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(body)), nil } 217 218 // set headers 219 hc.mu.Lock() 220 req.Header = hc.headers.Clone() 221 hc.mu.Unlock() 222 setHeaders(req.Header, headersFromContext(ctx)) 223 224 if hc.auth != nil { 225 if err := hc.auth(req.Header); err != nil { 226 return nil, err 227 } 228 } 229 230 // do request 231 resp, err := hc.client.Do(req) 232 if err != nil { 233 return nil, err 234 } 235 if resp.StatusCode < 200 || resp.StatusCode >= 300 { 236 var buf bytes.Buffer 237 var body []byte 238 if _, err := buf.ReadFrom(resp.Body); err == nil { 239 body = buf.Bytes() 240 } 241 242 return nil, HTTPError{ 243 Status: resp.Status, 244 StatusCode: resp.StatusCode, 245 Body: body, 246 } 247 } 248 return resp.Body, nil 249 } 250 251 // httpServerConn turns a HTTP connection into a Conn. 252 type httpServerConn struct { 253 io.Reader 254 io.Writer 255 r *http.Request 256 } 257 258 func newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec { 259 body := io.LimitReader(r.Body, maxRequestContentLength) 260 conn := &httpServerConn{Reader: body, Writer: w, r: r} 261 262 encoder := func(v any, isErrorResponse bool) error { 263 if !isErrorResponse { 264 return json.NewEncoder(conn).Encode(v) 265 } 266 267 // It's an error response and requires special treatment. 268 // 269 // In case of a timeout error, the response must be written before the HTTP 270 // server's write timeout occurs. So we need to flush the response. The 271 // Content-Length header also needs to be set to ensure the client knows 272 // when it has the full response. 273 encdata, err := json.Marshal(v) 274 if err != nil { 275 return err 276 } 277 w.Header().Set("content-length", strconv.Itoa(len(encdata))) 278 279 // If this request is wrapped in a handler that might remove Content-Length (such 280 // as the automatic gzip we do in package node), we need to ensure the HTTP server 281 // doesn't perform chunked encoding. In case WriteTimeout is reached, the chunked 282 // encoding might not be finished correctly, and some clients do not like it when 283 // the final chunk is missing. 284 w.Header().Set("transfer-encoding", "identity") 285 286 _, err = w.Write(encdata) 287 if f, ok := w.(http.Flusher); ok { 288 f.Flush() 289 } 290 return err 291 } 292 293 dec := json.NewDecoder(conn) 294 dec.UseNumber() 295 296 return NewFuncCodec(conn, encoder, dec.Decode) 297 } 298 299 // Close does nothing and always returns nil. 300 func (t *httpServerConn) Close() error { return nil } 301 302 // RemoteAddr returns the peer address of the underlying connection. 303 func (t *httpServerConn) RemoteAddr() string { 304 return t.r.RemoteAddr 305 } 306 307 // SetWriteDeadline does nothing and always returns nil. 308 func (t *httpServerConn) SetWriteDeadline(time.Time) error { return nil } 309 310 // ServeHTTP serves JSON-RPC requests over HTTP. 311 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 312 // Permit dumb empty requests for remote health-checks (AWS) 313 if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { 314 w.WriteHeader(http.StatusOK) 315 return 316 } 317 if code, err := validateRequest(r); err != nil { 318 http.Error(w, err.Error(), code) 319 return 320 } 321 322 // Create request-scoped context. 323 connInfo := PeerInfo{Transport: "http", RemoteAddr: r.RemoteAddr} 324 connInfo.HTTP.Version = r.Proto 325 connInfo.HTTP.Host = r.Host 326 connInfo.HTTP.Origin = r.Header.Get("Origin") 327 connInfo.HTTP.UserAgent = r.Header.Get("User-Agent") 328 ctx := r.Context() 329 ctx = context.WithValue(ctx, peerInfoContextKey{}, connInfo) 330 331 // All checks passed, create a codec that reads directly from the request body 332 // until EOF, writes the response to w, and orders the server to process a 333 // single request. 334 w.Header().Set("content-type", contentType) 335 codec := newHTTPServerConn(r, w) 336 defer codec.close() 337 s.serveSingleRequest(ctx, codec) 338 } 339 340 // validateRequest returns a non-zero response code and error message if the 341 // request is invalid. 342 func validateRequest(r *http.Request) (int, error) { 343 if r.Method == http.MethodPut || r.Method == http.MethodDelete { 344 return http.StatusMethodNotAllowed, errors.New("method not allowed") 345 } 346 if r.ContentLength > maxRequestContentLength { 347 err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength) 348 return http.StatusRequestEntityTooLarge, err 349 } 350 // Allow OPTIONS (regardless of content-type) 351 if r.Method == http.MethodOptions { 352 return 0, nil 353 } 354 // Check content-type 355 if mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")); err == nil { 356 for _, accepted := range acceptedContentTypes { 357 if accepted == mt { 358 return 0, nil 359 } 360 } 361 } 362 // Invalid content-type 363 err := fmt.Errorf("invalid content type, only %s is supported", contentType) 364 return http.StatusUnsupportedMediaType, err 365 } 366 367 // ContextRequestTimeout returns the request timeout derived from the given context. 368 func ContextRequestTimeout(ctx context.Context) (time.Duration, bool) { 369 timeout := time.Duration(math.MaxInt64) 370 hasTimeout := false 371 setTimeout := func(d time.Duration) { 372 if d < timeout { 373 timeout = d 374 hasTimeout = true 375 } 376 } 377 378 if deadline, ok := ctx.Deadline(); ok { 379 setTimeout(time.Until(deadline)) 380 } 381 382 // If the context is an HTTP request context, use the server's WriteTimeout. 383 httpSrv, ok := ctx.Value(http.ServerContextKey).(*http.Server) 384 if ok && httpSrv.WriteTimeout > 0 { 385 wt := httpSrv.WriteTimeout 386 // When a write timeout is configured, we need to send the response message before 387 // the HTTP server cuts connection. So our internal timeout must be earlier than 388 // the server's true timeout. 389 // 390 // Note: Timeouts are sanitized to be a minimum of 1 second. 391 // Also see issue: https://github.com/golang/go/issues/47229 392 wt -= 100 * time.Millisecond 393 setTimeout(wt) 394 } 395 396 return timeout, hasTimeout 397 }