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