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