github.com/codysnider/go-ethereum@v1.10.18-0.20220420071915-14f4ae99222a/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 req.GetBody = func() (io.ReadCloser, error) { return ioutil.NopCloser(bytes.NewReader(body)), nil } 185 186 // set headers 187 hc.mu.Lock() 188 req.Header = hc.headers.Clone() 189 hc.mu.Unlock() 190 191 // do request 192 resp, err := hc.client.Do(req) 193 if err != nil { 194 return nil, err 195 } 196 if resp.StatusCode < 200 || resp.StatusCode >= 300 { 197 var buf bytes.Buffer 198 var body []byte 199 if _, err := buf.ReadFrom(resp.Body); err == nil { 200 body = buf.Bytes() 201 } 202 203 return nil, HTTPError{ 204 Status: resp.Status, 205 StatusCode: resp.StatusCode, 206 Body: body, 207 } 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 NewCodec(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 // ServeHTTP serves JSON-RPC requests over HTTP. 237 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 238 // Permit dumb empty requests for remote health-checks (AWS) 239 if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { 240 w.WriteHeader(http.StatusOK) 241 return 242 } 243 if code, err := validateRequest(r); err != nil { 244 http.Error(w, err.Error(), code) 245 return 246 } 247 248 // Create request-scoped context. 249 connInfo := PeerInfo{Transport: "http", RemoteAddr: r.RemoteAddr} 250 connInfo.HTTP.Version = r.Proto 251 connInfo.HTTP.Host = r.Host 252 connInfo.HTTP.Origin = r.Header.Get("Origin") 253 connInfo.HTTP.UserAgent = r.Header.Get("User-Agent") 254 ctx := r.Context() 255 ctx = context.WithValue(ctx, peerInfoContextKey{}, connInfo) 256 257 // All checks passed, create a codec that reads directly from the request body 258 // until EOF, writes the response to w, and orders the server to process a 259 // single request. 260 w.Header().Set("content-type", contentType) 261 codec := newHTTPServerConn(r, w) 262 defer codec.close() 263 s.serveSingleRequest(ctx, codec) 264 } 265 266 // validateRequest returns a non-zero response code and error message if the 267 // request is invalid. 268 func validateRequest(r *http.Request) (int, error) { 269 if r.Method == http.MethodPut || r.Method == http.MethodDelete { 270 return http.StatusMethodNotAllowed, errors.New("method not allowed") 271 } 272 if r.ContentLength > maxRequestContentLength { 273 err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength) 274 return http.StatusRequestEntityTooLarge, err 275 } 276 // Allow OPTIONS (regardless of content-type) 277 if r.Method == http.MethodOptions { 278 return 0, nil 279 } 280 // Check content-type 281 if mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")); err == nil { 282 for _, accepted := range acceptedContentTypes { 283 if accepted == mt { 284 return 0, nil 285 } 286 } 287 } 288 // Invalid content-type 289 err := fmt.Errorf("invalid content type, only %s is supported", contentType) 290 return http.StatusUnsupportedMediaType, err 291 }