github.com/gochain-io/gochain@v2.2.26+incompatible/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" 29 "net/http" 30 "strings" 31 "sync" 32 "time" 33 34 "github.com/rs/cors" 35 "go.opencensus.io/plugin/ochttp" 36 "go.opencensus.io/trace" 37 38 "github.com/gochain-io/gochain/log" 39 ) 40 41 const ( 42 contentType = "application/json" 43 maxRequestContentLength = 1024 * 512 44 ) 45 46 var nullAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:0") 47 48 type httpConn struct { 49 client *http.Client 50 req *http.Request 51 closeOnce sync.Once 52 closed chan struct{} 53 } 54 55 // httpConn is treated specially by Client. 56 func (hc *httpConn) LocalAddr() net.Addr { return nullAddr } 57 func (hc *httpConn) RemoteAddr() net.Addr { return nullAddr } 58 func (hc *httpConn) SetReadDeadline(time.Time) error { return nil } 59 func (hc *httpConn) SetWriteDeadline(time.Time) error { return nil } 60 func (hc *httpConn) SetDeadline(time.Time) error { return nil } 61 func (hc *httpConn) Write([]byte) (int, error) { panic("Write called") } 62 63 func (hc *httpConn) Read(b []byte) (int, error) { 64 <-hc.closed 65 return 0, io.EOF 66 } 67 68 func (hc *httpConn) Close() error { 69 hc.closeOnce.Do(func() { close(hc.closed) }) 70 return nil 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 req, err := http.NewRequest(http.MethodPost, endpoint, nil) 109 if err != nil { 110 return nil, err 111 } 112 req.Header.Set("Content-Type", contentType) 113 req.Header.Set("Accept", contentType) 114 115 initctx := context.Background() 116 return newClient(initctx, func(context.Context) (net.Conn, error) { 117 return &httpConn{client: client, req: req, closed: make(chan struct{})}, nil 118 }) 119 } 120 121 // DialHTTP creates a new RPC client that connects to an RPC server over HTTP. 122 func DialHTTP(endpoint string) (*Client, error) { 123 return DialHTTPWithClient(endpoint, new(http.Client)) 124 } 125 126 func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { 127 hc := c.writeConn.(*httpConn) 128 respBody, err := hc.doRequest(ctx, msg) 129 if respBody != nil { 130 defer respBody.Close() 131 } 132 133 if err != nil { 134 if respBody != nil { 135 buf := new(bytes.Buffer) 136 if _, err2 := buf.ReadFrom(respBody); err2 == nil { 137 return fmt.Errorf("%v %v", err, buf.String()) 138 } 139 } 140 return err 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 := hc.req.WithContext(ctx) 173 req.Body = ioutil.NopCloser(bytes.NewReader(body)) 174 req.ContentLength = int64(len(body)) 175 176 resp, err := hc.client.Do(req) 177 if err != nil { 178 return nil, err 179 } 180 if resp.StatusCode < 200 || resp.StatusCode >= 300 { 181 return resp.Body, errors.New(resp.Status) 182 } 183 return resp.Body, nil 184 } 185 186 // httpReadWriteNopCloser wraps a io.Reader and io.Writer with a NOP Close method. 187 type httpReadWriteNopCloser struct { 188 io.Reader 189 io.Writer 190 } 191 192 // Close does nothing and returns always nil 193 func (t *httpReadWriteNopCloser) Close() error { 194 return nil 195 } 196 197 // NewHTTPServer creates a new HTTP RPC server around an API provider. 198 // 199 // Deprecated: Server implements http.Handler 200 func NewHTTPServer(cors []string, vhosts []string, timeouts HTTPTimeouts, srv *Server, tracing bool) *http.Server { 201 // Wrap the CORS-handler within a host-handler 202 handler := newCorsHandler(srv, cors) 203 handler = newVHostHandler(vhosts, handler) 204 if tracing { 205 handler = &ochttp.Handler{Handler: handler, IsPublicEndpoint: true} 206 } 207 // Make sure timeout values are meaningful 208 if timeouts.ReadTimeout < time.Second { 209 log.Warn("Sanitizing invalid HTTP read timeout", "provided", timeouts.ReadTimeout, "updated", DefaultHTTPTimeouts.ReadTimeout) 210 timeouts.ReadTimeout = DefaultHTTPTimeouts.ReadTimeout 211 } 212 if timeouts.WriteTimeout < time.Second { 213 log.Warn("Sanitizing invalid HTTP write timeout", "provided", timeouts.WriteTimeout, "updated", DefaultHTTPTimeouts.WriteTimeout) 214 timeouts.WriteTimeout = DefaultHTTPTimeouts.WriteTimeout 215 } 216 if timeouts.IdleTimeout < time.Second { 217 log.Warn("Sanitizing invalid HTTP idle timeout", "provided", timeouts.IdleTimeout, "updated", DefaultHTTPTimeouts.IdleTimeout) 218 timeouts.IdleTimeout = DefaultHTTPTimeouts.IdleTimeout 219 } 220 // Bundle and start the HTTP server 221 return &http.Server{ 222 Handler: handler, 223 ReadTimeout: timeouts.ReadTimeout, 224 WriteTimeout: timeouts.WriteTimeout, 225 IdleTimeout: timeouts.IdleTimeout, 226 } 227 } 228 229 // ServeHTTP serves JSON-RPC requests over HTTP. 230 func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 231 // Permit dumb empty requests for remote health-checks (AWS) 232 if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { 233 return 234 } 235 ctx := r.Context() 236 span := trace.FromContext(ctx) 237 if code, err := validateRequest(r); err != nil { 238 http.Error(w, err.Error(), code) 239 span.SetStatus(trace.Status{ 240 Code: trace.StatusCodeInvalidArgument, 241 Message: err.Error(), 242 }) 243 return 244 } 245 // All checks passed, create a codec that reads direct from the request body 246 // untilEOF and writes the response to w and order the server to process a 247 // single request. 248 ctx = context.WithValue(ctx, "remote", r.RemoteAddr) 249 ctx = context.WithValue(ctx, "scheme", r.Proto) 250 ctx = context.WithValue(ctx, "local", r.Host) 251 if ua := r.Header.Get("User-Agent"); ua != "" { 252 ctx = context.WithValue(ctx, "User-Agent", ua) 253 } 254 if origin := r.Header.Get("Origin"); origin != "" { 255 ctx = context.WithValue(ctx, "Origin", origin) 256 } 257 258 body := io.LimitReader(r.Body, maxRequestContentLength) 259 codec := NewJSONCodec(&httpReadWriteNopCloser{body, w}) 260 defer codec.Close() 261 262 w.Header().Set("content-type", contentType) 263 srv.ServeSingleRequest(ctx, codec, OptionMethodInvocation) 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 mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")) 277 if r.Method != http.MethodOptions && (err != nil || mt != contentType) { 278 err := fmt.Errorf("invalid content type, only %s is supported", contentType) 279 return http.StatusUnsupportedMediaType, err 280 } 281 return 0, nil 282 } 283 284 func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler { 285 // disable CORS support if user has not specified a custom CORS configuration 286 if len(allowedOrigins) == 0 { 287 return srv 288 } 289 c := cors.New(cors.Options{ 290 AllowedOrigins: allowedOrigins, 291 AllowedMethods: []string{http.MethodPost, http.MethodGet}, 292 MaxAge: 600, 293 AllowedHeaders: []string{"*"}, 294 }) 295 return c.Handler(srv) 296 } 297 298 // virtualHostHandler is a handler which validates the Host-header of incoming requests. 299 // The virtualHostHandler can prevent DNS rebinding attacks, which do not utilize CORS-headers, 300 // since they do in-domain requests against the RPC api. Instead, we can see on the Host-header 301 // which domain was used, and validate that against a whitelist. 302 type virtualHostHandler struct { 303 vhosts map[string]struct{} 304 next http.Handler 305 } 306 307 // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler 308 func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 309 // if r.Host is not set, we can continue serving since a browser would set the Host header 310 if r.Host == "" { 311 h.next.ServeHTTP(w, r) 312 return 313 } 314 host, _, err := net.SplitHostPort(r.Host) 315 if err != nil { 316 // Either invalid (too many colons) or no port specified 317 host = r.Host 318 } 319 if ipAddr := net.ParseIP(host); ipAddr != nil { 320 // It's an IP address, we can serve that 321 h.next.ServeHTTP(w, r) 322 return 323 324 } 325 // Not an ip address, but a hostname. Need to validate 326 if h.validHost(host) { 327 h.next.ServeHTTP(w, r) 328 return 329 } 330 http.Error(w, "invalid host specified", http.StatusForbidden) 331 } 332 333 func (h *virtualHostHandler) validHost(host string) bool { 334 if _, exist := h.vhosts["*"]; exist { 335 return true 336 } 337 if _, exist := h.vhosts[host]; exist { 338 return true 339 } 340 for i := strings.IndexByte(host, '.'); i > -1; i = strings.IndexByte(host, '.') { 341 if _, exist := h.vhosts["*"+host[i:]]; exist { 342 return true 343 } 344 host = host[i+1:] // Drop leading '.'. 345 } 346 return false 347 } 348 349 func newVHostHandler(vhosts []string, next http.Handler) *virtualHostHandler { 350 vhostMap := make(map[string]struct{}) 351 for _, allowedHost := range vhosts { 352 vhostMap[strings.ToLower(allowedHost)] = struct{}{} 353 } 354 return &virtualHostHandler{vhostMap, next} 355 }