github.com/bcnmy/go-ethereum@v1.10.27/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 "mime" 27 "net/http" 28 "net/url" 29 "sync" 30 "time" 31 ) 32 33 const ( 34 maxRequestContentLength = 1024 * 1024 * 5 35 contentType = "application/json" 36 ) 37 38 // https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13 39 var acceptedContentTypes = []string{contentType, "application/json-rpc", "application/jsonrequest"} 40 41 type httpConn struct { 42 client *http.Client 43 url string 44 closeOnce sync.Once 45 closeCh chan interface{} 46 mu sync.Mutex // protects headers 47 headers http.Header 48 } 49 50 // httpConn implements ServerCodec, but it is treated specially by Client 51 // and some methods don't work. The panic() stubs here exist to ensure 52 // this special treatment is correct. 53 54 func (hc *httpConn) writeJSON(context.Context, interface{}) error { 55 panic("writeJSON called on httpConn") 56 } 57 58 func (hc *httpConn) peerInfo() PeerInfo { 59 panic("peerInfo called on httpConn") 60 } 61 62 func (hc *httpConn) remoteAddr() string { 63 return hc.url 64 } 65 66 func (hc *httpConn) readBatch() ([]*jsonrpcMessage, bool, error) { 67 <-hc.closeCh 68 return nil, false, io.EOF 69 } 70 71 func (hc *httpConn) close() { 72 hc.closeOnce.Do(func() { close(hc.closeCh) }) 73 } 74 75 func (hc *httpConn) closed() <-chan interface{} { 76 return hc.closeCh 77 } 78 79 // HTTPTimeouts represents the configuration params for the HTTP RPC server. 80 type HTTPTimeouts struct { 81 // ReadTimeout is the maximum duration for reading the entire 82 // request, including the body. 83 // 84 // Because ReadTimeout does not let Handlers make per-request 85 // decisions on each request body's acceptable deadline or 86 // upload rate, most users will prefer to use 87 // ReadHeaderTimeout. It is valid to use them both. 88 ReadTimeout time.Duration 89 90 // ReadHeaderTimeout is the amount of time allowed to read 91 // request headers. The connection's read deadline is reset 92 // after reading the headers and the Handler can decide what 93 // is considered too slow for the body. If ReadHeaderTimeout 94 // is zero, the value of ReadTimeout is used. If both are 95 // zero, there is no timeout. 96 ReadHeaderTimeout time.Duration 97 98 // WriteTimeout is the maximum duration before timing out 99 // writes of the response. It is reset whenever a new 100 // request's header is read. Like ReadTimeout, it does not 101 // let Handlers make decisions on a per-request basis. 102 WriteTimeout time.Duration 103 104 // IdleTimeout is the maximum amount of time to wait for the 105 // next request when keep-alives are enabled. If IdleTimeout 106 // is zero, the value of ReadTimeout is used. If both are 107 // zero, ReadHeaderTimeout is used. 108 IdleTimeout time.Duration 109 } 110 111 // DefaultHTTPTimeouts represents the default timeout values used if further 112 // configuration is not provided. 113 var DefaultHTTPTimeouts = HTTPTimeouts{ 114 ReadTimeout: 30 * time.Second, 115 ReadHeaderTimeout: 30 * time.Second, 116 WriteTimeout: 30 * time.Second, 117 IdleTimeout: 120 * time.Second, 118 } 119 120 // DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP 121 // using the provided HTTP Client. 122 func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { 123 // Sanity check URL so we don't end up with a client that will fail every request. 124 _, err := url.Parse(endpoint) 125 if err != nil { 126 return nil, err 127 } 128 129 initctx := context.Background() 130 headers := make(http.Header, 2) 131 headers.Set("accept", contentType) 132 headers.Set("content-type", contentType) 133 return newClient(initctx, func(context.Context) (ServerCodec, error) { 134 hc := &httpConn{ 135 client: client, 136 headers: headers, 137 url: endpoint, 138 closeCh: make(chan interface{}), 139 } 140 return hc, nil 141 }) 142 } 143 144 // DialHTTP creates a new RPC client that connects to an RPC server over HTTP. 145 func DialHTTP(endpoint string) (*Client, error) { 146 return DialHTTPWithClient(endpoint, new(http.Client)) 147 } 148 149 func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { 150 hc := c.writeConn.(*httpConn) 151 respBody, err := hc.doRequest(ctx, msg) 152 if err != nil { 153 return err 154 } 155 defer respBody.Close() 156 157 var respmsg jsonrpcMessage 158 if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil { 159 return err 160 } 161 op.resp <- &respmsg 162 return nil 163 } 164 165 func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonrpcMessage) error { 166 hc := c.writeConn.(*httpConn) 167 respBody, err := hc.doRequest(ctx, msgs) 168 if err != nil { 169 return err 170 } 171 defer respBody.Close() 172 var respmsgs []jsonrpcMessage 173 if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { 174 return err 175 } 176 if len(respmsgs) != len(msgs) { 177 return fmt.Errorf("batch has %d requests but response has %d: %w", len(msgs), len(respmsgs), ErrBadResult) 178 } 179 for i := 0; i < len(respmsgs); i++ { 180 op.resp <- &respmsgs[i] 181 } 182 return nil 183 } 184 185 func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadCloser, error) { 186 body, err := json.Marshal(msg) 187 if err != nil { 188 return nil, err 189 } 190 req, err := http.NewRequestWithContext(ctx, "POST", hc.url, io.NopCloser(bytes.NewReader(body))) 191 if err != nil { 192 return nil, err 193 } 194 req.ContentLength = int64(len(body)) 195 req.GetBody = func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(body)), nil } 196 197 // set headers 198 hc.mu.Lock() 199 req.Header = hc.headers.Clone() 200 hc.mu.Unlock() 201 202 // do request 203 resp, err := hc.client.Do(req) 204 if err != nil { 205 return nil, err 206 } 207 if resp.StatusCode < 200 || resp.StatusCode >= 300 { 208 var buf bytes.Buffer 209 var body []byte 210 if _, err := buf.ReadFrom(resp.Body); err == nil { 211 body = buf.Bytes() 212 } 213 214 return nil, HTTPError{ 215 Status: resp.Status, 216 StatusCode: resp.StatusCode, 217 Body: body, 218 } 219 } 220 return resp.Body, nil 221 } 222 223 // httpServerConn turns a HTTP connection into a Conn. 224 type httpServerConn struct { 225 io.Reader 226 io.Writer 227 r *http.Request 228 } 229 230 func newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec { 231 body := io.LimitReader(r.Body, maxRequestContentLength) 232 conn := &httpServerConn{Reader: body, Writer: w, r: r} 233 return NewCodec(conn) 234 } 235 236 // Close does nothing and always returns nil. 237 func (t *httpServerConn) Close() error { return nil } 238 239 // RemoteAddr returns the peer address of the underlying connection. 240 func (t *httpServerConn) RemoteAddr() string { 241 return t.r.RemoteAddr 242 } 243 244 // SetWriteDeadline does nothing and always returns nil. 245 func (t *httpServerConn) SetWriteDeadline(time.Time) error { return nil } 246 247 // ServeHTTP serves JSON-RPC requests over HTTP. 248 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 249 // Permit dumb empty requests for remote health-checks (AWS) 250 if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { 251 w.WriteHeader(http.StatusOK) 252 return 253 } 254 if code, err := validateRequest(r); err != nil { 255 http.Error(w, err.Error(), code) 256 return 257 } 258 259 // Create request-scoped context. 260 connInfo := PeerInfo{Transport: "http", RemoteAddr: r.RemoteAddr} 261 connInfo.HTTP.Version = r.Proto 262 connInfo.HTTP.Host = r.Host 263 connInfo.HTTP.Origin = r.Header.Get("Origin") 264 connInfo.HTTP.UserAgent = r.Header.Get("User-Agent") 265 ctx := r.Context() 266 ctx = context.WithValue(ctx, peerInfoContextKey{}, connInfo) 267 268 // All checks passed, create a codec that reads directly from the request body 269 // until EOF, writes the response to w, and orders the server to process a 270 // single request. 271 w.Header().Set("content-type", contentType) 272 codec := newHTTPServerConn(r, w) 273 defer codec.close() 274 s.serveSingleRequest(ctx, codec) 275 } 276 277 // validateRequest returns a non-zero response code and error message if the 278 // request is invalid. 279 func validateRequest(r *http.Request) (int, error) { 280 if r.Method == http.MethodPut || r.Method == http.MethodDelete { 281 return http.StatusMethodNotAllowed, errors.New("method not allowed") 282 } 283 if r.ContentLength > maxRequestContentLength { 284 err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength) 285 return http.StatusRequestEntityTooLarge, err 286 } 287 // Allow OPTIONS (regardless of content-type) 288 if r.Method == http.MethodOptions { 289 return 0, nil 290 } 291 // Check content-type 292 if mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")); err == nil { 293 for _, accepted := range acceptedContentTypes { 294 if accepted == mt { 295 return 0, nil 296 } 297 } 298 } 299 // Invalid content-type 300 err := fmt.Errorf("invalid content type, only %s is supported", contentType) 301 return http.StatusUnsupportedMediaType, err 302 }