github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.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" 29 "net/http" 30 "strings" 31 "sync" 32 "time" 33 34 "github.com/rs/cors" 35 ) 36 37 const ( 38 contentType = "application/json" 39 maxRequestContentLength = 1024 * 128 40 ) 41 42 var nullAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:0") 43 44 type httpConn struct { 45 client *http.Client 46 req *http.Request 47 closeOnce sync.Once 48 closed chan struct{} 49 } 50 51 // httpConn is treated specially by Client. 52 func (hc *httpConn) LocalAddr() net.Addr { return nullAddr } 53 func (hc *httpConn) RemoteAddr() net.Addr { return nullAddr } 54 func (hc *httpConn) SetReadDeadline(time.Time) error { return nil } 55 func (hc *httpConn) SetWriteDeadline(time.Time) error { return nil } 56 func (hc *httpConn) SetDeadline(time.Time) error { return nil } 57 func (hc *httpConn) Write([]byte) (int, error) { panic("Write called") } 58 59 func (hc *httpConn) Read(b []byte) (int, error) { 60 <-hc.closed 61 return 0, io.EOF 62 } 63 64 func (hc *httpConn) Close() error { 65 hc.closeOnce.Do(func() { close(hc.closed) }) 66 return nil 67 } 68 69 // DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP 70 // using the provided HTTP Client. 71 func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { 72 req, err := http.NewRequest(http.MethodPost, endpoint, nil) 73 if err != nil { 74 return nil, err 75 } 76 req.Header.Set("Content-Type", contentType) 77 req.Header.Set("Accept", contentType) 78 79 initctx := context.Background() 80 return newClient(initctx, func(context.Context) (net.Conn, error) { 81 return &httpConn{client: client, req: req, closed: make(chan struct{})}, nil 82 }) 83 } 84 85 // DialHTTP creates a new RPC client that connects to an RPC server over HTTP. 86 func DialHTTP(endpoint string) (*Client, error) { 87 return DialHTTPWithClient(endpoint, new(http.Client)) 88 } 89 90 func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { 91 hc := c.writeConn.(*httpConn) 92 respBody, err := hc.doRequest(ctx, msg) 93 if respBody != nil { 94 defer respBody.Close() 95 } 96 97 if err != nil { 98 if respBody != nil { 99 buf := new(bytes.Buffer) 100 if _, err2 := buf.ReadFrom(respBody); err2 == nil { 101 return fmt.Errorf("%v %v", err, buf.String()) 102 } 103 } 104 return err 105 } 106 var respmsg jsonrpcMessage 107 if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil { 108 return err 109 } 110 op.resp <- &respmsg 111 return nil 112 } 113 114 func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonrpcMessage) error { 115 hc := c.writeConn.(*httpConn) 116 respBody, err := hc.doRequest(ctx, msgs) 117 if err != nil { 118 return err 119 } 120 defer respBody.Close() 121 var respmsgs []jsonrpcMessage 122 if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { 123 return err 124 } 125 for i := 0; i < len(respmsgs); i++ { 126 op.resp <- &respmsgs[i] 127 } 128 return nil 129 } 130 131 func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadCloser, error) { 132 body, err := json.Marshal(msg) 133 if err != nil { 134 return nil, err 135 } 136 req := hc.req.WithContext(ctx) 137 req.Body = ioutil.NopCloser(bytes.NewReader(body)) 138 req.ContentLength = int64(len(body)) 139 140 resp, err := hc.client.Do(req) 141 if err != nil { 142 return nil, err 143 } 144 if resp.StatusCode < 200 || resp.StatusCode >= 300 { 145 return resp.Body, errors.New(resp.Status) 146 } 147 return resp.Body, nil 148 } 149 150 // httpReadWriteNopCloser wraps a io.Reader and io.Writer with a NOP Close method. 151 type httpReadWriteNopCloser struct { 152 io.Reader 153 io.Writer 154 } 155 156 // Close does nothing and returns always nil 157 func (t *httpReadWriteNopCloser) Close() error { 158 return nil 159 } 160 161 // NewHTTPServer creates a new HTTP RPC server around an API provider. 162 // 163 // Deprecated: Server implements http.Handler 164 func NewHTTPServer(cors []string, vhosts []string, srv *Server) *http.Server { 165 // Wrap the CORS-handler within a host-handler 166 handler := newCorsHandler(srv, cors) 167 handler = newVHostHandler(vhosts, handler) 168 return &http.Server{ 169 Handler: handler, 170 ReadTimeout: 5 * time.Second, 171 WriteTimeout: 10 * time.Second, 172 IdleTimeout: 120 * time.Second, 173 } 174 } 175 176 // ServeHTTP serves JSON-RPC requests over HTTP. 177 func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 178 // Permit dumb empty requests for remote health-checks (AWS) 179 if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { 180 return 181 } 182 if code, err := validateRequest(r); err != nil { 183 http.Error(w, err.Error(), code) 184 return 185 } 186 // All checks passed, create a codec that reads direct from the request body 187 // untilEOF and writes the response to w and order the server to process a 188 // single request. 189 ctx := r.Context() 190 ctx = context.WithValue(ctx, "remote", r.RemoteAddr) 191 ctx = context.WithValue(ctx, "scheme", r.Proto) 192 ctx = context.WithValue(ctx, "local", r.Host) 193 194 body := io.LimitReader(r.Body, maxRequestContentLength) 195 codec := NewJSONCodec(&httpReadWriteNopCloser{body, w}) 196 defer codec.Close() 197 198 w.Header().Set("content-type", contentType) 199 srv.ServeSingleRequest(ctx, codec, OptionMethodInvocation) 200 } 201 202 // validateRequest returns a non-zero response code and error message if the 203 // request is invalid. 204 func validateRequest(r *http.Request) (int, error) { 205 if r.Method == http.MethodPut || r.Method == http.MethodDelete { 206 return http.StatusMethodNotAllowed, errors.New("method not allowed") 207 } 208 if r.ContentLength > maxRequestContentLength { 209 err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength) 210 return http.StatusRequestEntityTooLarge, err 211 } 212 mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")) 213 if r.Method != http.MethodOptions && (err != nil || mt != contentType) { 214 err := fmt.Errorf("invalid content type, only %s is supported", contentType) 215 return http.StatusUnsupportedMediaType, err 216 } 217 return 0, nil 218 } 219 220 func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler { 221 // disable CORS support if user has not specified a custom CORS configuration 222 if len(allowedOrigins) == 0 { 223 return srv 224 } 225 c := cors.New(cors.Options{ 226 AllowedOrigins: allowedOrigins, 227 AllowedMethods: []string{http.MethodPost, http.MethodGet}, 228 MaxAge: 600, 229 AllowedHeaders: []string{"*"}, 230 }) 231 return c.Handler(srv) 232 } 233 234 // virtualHostHandler is a handler which validates the Host-header of incoming requests. 235 // The virtualHostHandler can prevent DNS rebinding attacks, which do not utilize CORS-headers, 236 // since they do in-domain requests against the RPC api. Instead, we can see on the Host-header 237 // which domain was used, and validate that against a whitelist. 238 type virtualHostHandler struct { 239 vhosts map[string]struct{} 240 next http.Handler 241 } 242 243 // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler 244 func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 245 // if r.Host is not set, we can continue serving since a browser would set the Host header 246 if r.Host == "" { 247 h.next.ServeHTTP(w, r) 248 return 249 } 250 host, _, err := net.SplitHostPort(r.Host) 251 if err != nil { 252 // Either invalid (too many colons) or no port specified 253 host = r.Host 254 } 255 if ipAddr := net.ParseIP(host); ipAddr != nil { 256 // It's an IP address, we can serve that 257 h.next.ServeHTTP(w, r) 258 return 259 260 } 261 // Not an ip address, but a hostname. Need to validate 262 if _, exist := h.vhosts["*"]; exist { 263 h.next.ServeHTTP(w, r) 264 return 265 } 266 if _, exist := h.vhosts[host]; exist { 267 h.next.ServeHTTP(w, r) 268 return 269 } 270 http.Error(w, "invalid host specified", http.StatusForbidden) 271 } 272 273 func newVHostHandler(vhosts []string, next http.Handler) http.Handler { 274 vhostMap := make(map[string]struct{}) 275 for _, allowedHost := range vhosts { 276 vhostMap[strings.ToLower(allowedHost)] = struct{}{} 277 } 278 return &virtualHostHandler{vhostMap, next} 279 }