github.com/needkane/go-ethereum@v1.7.4-0.20180131070256-c212876ea2b7/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 "sync" 31 "time" 32 33 "github.com/rs/cors" 34 ) 35 36 const ( 37 contentType = "application/json" 38 maxHTTPRequestContentLength = 1024 * 128 39 ) 40 41 var nullAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:0") 42 43 type httpConn struct { 44 client *http.Client 45 req *http.Request 46 closeOnce sync.Once 47 closed chan struct{} 48 } 49 50 // httpConn is treated specially by Client. 51 func (hc *httpConn) LocalAddr() net.Addr { return nullAddr } 52 func (hc *httpConn) RemoteAddr() net.Addr { return nullAddr } 53 func (hc *httpConn) SetReadDeadline(time.Time) error { return nil } 54 func (hc *httpConn) SetWriteDeadline(time.Time) error { return nil } 55 func (hc *httpConn) SetDeadline(time.Time) error { return nil } 56 func (hc *httpConn) Write([]byte) (int, error) { panic("Write called") } 57 58 func (hc *httpConn) Read(b []byte) (int, error) { 59 <-hc.closed 60 return 0, io.EOF 61 } 62 63 func (hc *httpConn) Close() error { 64 hc.closeOnce.Do(func() { close(hc.closed) }) 65 return nil 66 } 67 68 // DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP 69 // using the provided HTTP Client. 70 func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { 71 req, err := http.NewRequest(http.MethodPost, endpoint, nil) 72 if err != nil { 73 return nil, err 74 } 75 req.Header.Set("Content-Type", contentType) 76 req.Header.Set("Accept", contentType) 77 78 initctx := context.Background() 79 return newClient(initctx, func(context.Context) (net.Conn, error) { 80 return &httpConn{client: client, req: req, closed: make(chan struct{})}, nil 81 }) 82 } 83 84 // DialHTTP creates a new RPC client that connects to an RPC server over HTTP. 85 func DialHTTP(endpoint string) (*Client, error) { 86 return DialHTTPWithClient(endpoint, new(http.Client)) 87 } 88 89 func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { 90 hc := c.writeConn.(*httpConn) 91 respBody, err := hc.doRequest(ctx, msg) 92 if err != nil { 93 return err 94 } 95 defer respBody.Close() 96 var respmsg jsonrpcMessage 97 if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil { 98 return err 99 } 100 op.resp <- &respmsg 101 return nil 102 } 103 104 func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonrpcMessage) error { 105 hc := c.writeConn.(*httpConn) 106 respBody, err := hc.doRequest(ctx, msgs) 107 if err != nil { 108 return err 109 } 110 defer respBody.Close() 111 var respmsgs []jsonrpcMessage 112 if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { 113 return err 114 } 115 for i := 0; i < len(respmsgs); i++ { 116 op.resp <- &respmsgs[i] 117 } 118 return nil 119 } 120 121 func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadCloser, error) { 122 body, err := json.Marshal(msg) 123 if err != nil { 124 return nil, err 125 } 126 req := hc.req.WithContext(ctx) 127 req.Body = ioutil.NopCloser(bytes.NewReader(body)) 128 req.ContentLength = int64(len(body)) 129 130 resp, err := hc.client.Do(req) 131 if err != nil { 132 return nil, err 133 } 134 return resp.Body, nil 135 } 136 137 // httpReadWriteNopCloser wraps a io.Reader and io.Writer with a NOP Close method. 138 type httpReadWriteNopCloser struct { 139 io.Reader 140 io.Writer 141 } 142 143 // Close does nothing and returns always nil 144 func (t *httpReadWriteNopCloser) Close() error { 145 return nil 146 } 147 148 // NewHTTPServer creates a new HTTP RPC server around an API provider. 149 // 150 // Deprecated: Server implements http.Handler 151 func NewHTTPServer(cors []string, srv *Server) *http.Server { 152 return &http.Server{Handler: newCorsHandler(srv, cors)} 153 } 154 155 // ServeHTTP serves JSON-RPC requests over HTTP. 156 func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 157 // Permit dumb empty requests for remote health-checks (AWS) 158 if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { 159 return 160 } 161 if code, err := validateRequest(r); err != nil { 162 http.Error(w, err.Error(), code) 163 return 164 } 165 // All checks passed, create a codec that reads direct from the request body 166 // untilEOF and writes the response to w and order the server to process a 167 // single request. 168 codec := NewJSONCodec(&httpReadWriteNopCloser{r.Body, w}) 169 defer codec.Close() 170 171 w.Header().Set("content-type", contentType) 172 srv.ServeSingleRequest(codec, OptionMethodInvocation) 173 } 174 175 // validateRequest returns a non-zero response code and error message if the 176 // request is invalid. 177 func validateRequest(r *http.Request) (int, error) { 178 if r.Method == http.MethodPut || r.Method == http.MethodDelete { 179 return http.StatusMethodNotAllowed, errors.New("method not allowed") 180 } 181 if r.ContentLength > maxHTTPRequestContentLength { 182 err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxHTTPRequestContentLength) 183 return http.StatusRequestEntityTooLarge, err 184 } 185 mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")) 186 if r.Method != http.MethodOptions && (err != nil || mt != contentType) { 187 err := fmt.Errorf("invalid content type, only %s is supported", contentType) 188 return http.StatusUnsupportedMediaType, err 189 } 190 return 0, nil 191 } 192 193 func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler { 194 // disable CORS support if user has not specified a custom CORS configuration 195 if len(allowedOrigins) == 0 { 196 return srv 197 } 198 199 c := cors.New(cors.Options{ 200 AllowedOrigins: allowedOrigins, 201 AllowedMethods: []string{http.MethodPost, http.MethodGet}, 202 MaxAge: 600, 203 AllowedHeaders: []string{"*"}, 204 }) 205 return c.Handler(srv) 206 }