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