github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/rpc/http.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:42</date> 10 //</624450108775206912> 11 12 13 package rpc 14 15 import ( 16 "bytes" 17 "context" 18 "encoding/json" 19 "errors" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "mime" 24 "net" 25 "net/http" 26 "strings" 27 "sync" 28 "time" 29 30 "github.com/ethereum/go-ethereum/log" 31 "github.com/rs/cors" 32 ) 33 34 const ( 35 maxRequestContentLength = 1024 * 512 36 ) 37 38 var ( 39 //https://www.json rpc.org/historical/json-rpc over http.html id13 40 acceptedContentTypes = []string{"application/json", "application/json-rpc", "application/jsonrequest"} 41 contentType = acceptedContentTypes[0] 42 nullAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:0") 43 ) 44 45 type httpConn struct { 46 client *http.Client 47 req *http.Request 48 closeOnce sync.Once 49 closed chan struct{} 50 } 51 52 //HTTPconn由客户特别处理。 53 func (hc *httpConn) LocalAddr() net.Addr { return nullAddr } 54 func (hc *httpConn) RemoteAddr() net.Addr { return nullAddr } 55 func (hc *httpConn) SetReadDeadline(time.Time) error { return nil } 56 func (hc *httpConn) SetWriteDeadline(time.Time) error { return nil } 57 func (hc *httpConn) SetDeadline(time.Time) error { return nil } 58 func (hc *httpConn) Write([]byte) (int, error) { panic("Write called") } 59 60 func (hc *httpConn) Read(b []byte) (int, error) { 61 <-hc.closed 62 return 0, io.EOF 63 } 64 65 func (hc *httpConn) Close() error { 66 hc.closeOnce.Do(func() { close(hc.closed) }) 67 return nil 68 } 69 70 //httpTimeouts表示HTTP RPC服务器的配置参数。 71 type HTTPTimeouts struct { 72 //readTimeout是读取整个 73 //请求,包括正文。 74 // 75 //因为readTimeout不允许处理程序按请求执行 76 //对每个请求主体可接受的截止日期或 77 //上传率,大多数用户更喜欢使用 78 //读取headerTimeout。两者都用是有效的。 79 ReadTimeout time.Duration 80 81 //WriteTimeout是超时前的最长持续时间 82 //写入响应。每当有新的 83 //请求的头被读取。和readTimeout一样,它没有 84 //让处理程序基于每个请求做出决策。 85 WriteTimeout time.Duration 86 87 //IdleTimeout是等待 88 //启用keep alives时的下一个请求。如果IdleTimeout 89 //为零,使用readTimeout的值。如果两者都是 90 //零,使用readHeaderTimeout。 91 IdleTimeout time.Duration 92 } 93 94 //DefaultHTTPTimeouts表示进一步使用的默认超时值 95 //未提供配置。 96 var DefaultHTTPTimeouts = HTTPTimeouts{ 97 ReadTimeout: 30 * time.Second, 98 WriteTimeout: 30 * time.Second, 99 IdleTimeout: 120 * time.Second, 100 } 101 102 //dialhttpwithclient创建通过HTTP连接到RPC服务器的新RPC客户端 103 //使用提供的HTTP客户端。 104 func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { 105 req, err := http.NewRequest(http.MethodPost, endpoint, nil) 106 if err != nil { 107 return nil, err 108 } 109 req.Header.Set("Content-Type", contentType) 110 req.Header.Set("Accept", contentType) 111 112 initctx := context.Background() 113 return newClient(initctx, func(context.Context) (net.Conn, error) { 114 return &httpConn{client: client, req: req, closed: make(chan struct{})}, nil 115 }) 116 } 117 118 //DialHTTP创建一个新的RPC客户端,通过HTTP连接到一个RPC服务器。 119 func DialHTTP(endpoint string) (*Client, error) { 120 return DialHTTPWithClient(endpoint, new(http.Client)) 121 } 122 123 func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { 124 hc := c.writeConn.(*httpConn) 125 respBody, err := hc.doRequest(ctx, msg) 126 if respBody != nil { 127 defer respBody.Close() 128 } 129 130 if err != nil { 131 if respBody != nil { 132 buf := new(bytes.Buffer) 133 if _, err2 := buf.ReadFrom(respBody); err2 == nil { 134 return fmt.Errorf("%v %v", err, buf.String()) 135 } 136 } 137 return err 138 } 139 var respmsg jsonrpcMessage 140 if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil { 141 return err 142 } 143 op.resp <- &respmsg 144 return nil 145 } 146 147 func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonrpcMessage) error { 148 hc := c.writeConn.(*httpConn) 149 respBody, err := hc.doRequest(ctx, msgs) 150 if err != nil { 151 return err 152 } 153 defer respBody.Close() 154 var respmsgs []jsonrpcMessage 155 if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { 156 return err 157 } 158 for i := 0; i < len(respmsgs); i++ { 159 op.resp <- &respmsgs[i] 160 } 161 return nil 162 } 163 164 func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadCloser, error) { 165 body, err := json.Marshal(msg) 166 if err != nil { 167 return nil, err 168 } 169 req := hc.req.WithContext(ctx) 170 req.Body = ioutil.NopCloser(bytes.NewReader(body)) 171 req.ContentLength = int64(len(body)) 172 173 resp, err := hc.client.Do(req) 174 if err != nil { 175 return nil, err 176 } 177 if resp.StatusCode < 200 || resp.StatusCode >= 300 { 178 return resp.Body, errors.New(resp.Status) 179 } 180 return resp.Body, nil 181 } 182 183 //httpreadwritenocloser使用nop close方法包装IO.reader和IO.writer。 184 type httpReadWriteNopCloser struct { 185 io.Reader 186 io.Writer 187 } 188 189 //CLOSE什么也不做,返回的总是零 190 func (t *httpReadWriteNopCloser) Close() error { 191 return nil 192 } 193 194 //new http server围绕API提供程序创建一个新的HTTP RPC服务器。 195 // 196 //已弃用:服务器实现http.handler 197 func NewHTTPServer(cors []string, vhosts []string, timeouts HTTPTimeouts, srv *Server) *http.Server { 198 //在主机处理程序中包装CORS处理程序 199 handler := newCorsHandler(srv, cors) 200 handler = newVHostHandler(vhosts, handler) 201 202 //确保超时值有意义 203 if timeouts.ReadTimeout < time.Second { 204 log.Warn("Sanitizing invalid HTTP read timeout", "provided", timeouts.ReadTimeout, "updated", DefaultHTTPTimeouts.ReadTimeout) 205 timeouts.ReadTimeout = DefaultHTTPTimeouts.ReadTimeout 206 } 207 if timeouts.WriteTimeout < time.Second { 208 log.Warn("Sanitizing invalid HTTP write timeout", "provided", timeouts.WriteTimeout, "updated", DefaultHTTPTimeouts.WriteTimeout) 209 timeouts.WriteTimeout = DefaultHTTPTimeouts.WriteTimeout 210 } 211 if timeouts.IdleTimeout < time.Second { 212 log.Warn("Sanitizing invalid HTTP idle timeout", "provided", timeouts.IdleTimeout, "updated", DefaultHTTPTimeouts.IdleTimeout) 213 timeouts.IdleTimeout = DefaultHTTPTimeouts.IdleTimeout 214 } 215 //捆绑并启动HTTP服务器 216 return &http.Server{ 217 Handler: handler, 218 ReadTimeout: timeouts.ReadTimeout, 219 WriteTimeout: timeouts.WriteTimeout, 220 IdleTimeout: timeouts.IdleTimeout, 221 } 222 } 223 224 //servehtp通过HTTP服务JSON-RPC请求。 225 func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 226 //允许远程健康检查(AWS)的哑空请求 227 if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { 228 return 229 } 230 if code, err := validateRequest(r); err != nil { 231 http.Error(w, err.Error(), code) 232 return 233 } 234 //通过所有检查,创建一个直接从请求主体读取的编解码器 235 //直到并将响应写入w,然后命令服务器处理 236 //单一请求。 237 ctx := r.Context() 238 ctx = context.WithValue(ctx, "remote", r.RemoteAddr) 239 ctx = context.WithValue(ctx, "scheme", r.Proto) 240 ctx = context.WithValue(ctx, "local", r.Host) 241 if ua := r.Header.Get("User-Agent"); ua != "" { 242 ctx = context.WithValue(ctx, "User-Agent", ua) 243 } 244 if origin := r.Header.Get("Origin"); origin != "" { 245 ctx = context.WithValue(ctx, "Origin", origin) 246 } 247 248 body := io.LimitReader(r.Body, maxRequestContentLength) 249 codec := NewJSONCodec(&httpReadWriteNopCloser{body, w}) 250 defer codec.Close() 251 252 w.Header().Set("content-type", contentType) 253 srv.ServeSingleRequest(ctx, codec, OptionMethodInvocation) 254 } 255 256 //validateRequest返回非零响应代码和错误消息,如果 257 //请求无效。 258 func validateRequest(r *http.Request) (int, error) { 259 if r.Method == http.MethodPut || r.Method == http.MethodDelete { 260 return http.StatusMethodNotAllowed, errors.New("method not allowed") 261 } 262 if r.ContentLength > maxRequestContentLength { 263 err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength) 264 return http.StatusRequestEntityTooLarge, err 265 } 266 //允许选项(不考虑内容类型) 267 if r.Method == http.MethodOptions { 268 return 0, nil 269 } 270 //检查内容类型 271 if mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")); err == nil { 272 for _, accepted := range acceptedContentTypes { 273 if accepted == mt { 274 return 0, nil 275 } 276 } 277 } 278 //内容类型无效 279 err := fmt.Errorf("invalid content type, only %s is supported", contentType) 280 return http.StatusUnsupportedMediaType, err 281 } 282 283 func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler { 284 //如果用户未指定自定义CORS配置,则禁用CORS支持 285 if len(allowedOrigins) == 0 { 286 return srv 287 } 288 c := cors.New(cors.Options{ 289 AllowedOrigins: allowedOrigins, 290 AllowedMethods: []string{http.MethodPost, http.MethodGet}, 291 MaxAge: 600, 292 AllowedHeaders: []string{"*"}, 293 }) 294 return c.Handler(srv) 295 } 296 297 //virtualHostHandler是验证传入请求的主机头的处理程序。 298 //virtualHostHandler可以防止不使用CORS头的DNS重新绑定攻击, 299 //因为它们是针对RPC API的域请求。相反,我们可以在主机头上看到 300 //使用了哪个域,并根据白名单验证这一点。 301 type virtualHostHandler struct { 302 vhosts map[string]struct{} 303 next http.Handler 304 } 305 306 //servehtp通过http服务json-rpc请求,实现http.handler 307 func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 308 //如果没有设置r.host,我们可以继续服务,因为浏览器会设置主机头 309 if r.Host == "" { 310 h.next.ServeHTTP(w, r) 311 return 312 } 313 host, _, err := net.SplitHostPort(r.Host) 314 if err != nil { 315 //无效(冒号太多)或未指定端口 316 host = r.Host 317 } 318 if ipAddr := net.ParseIP(host); ipAddr != nil { 319 //这是一个IP地址,我们可以提供 320 h.next.ServeHTTP(w, r) 321 return 322 323 } 324 //不是IP地址,而是主机名。需要验证 325 if _, exist := h.vhosts["*"]; exist { 326 h.next.ServeHTTP(w, r) 327 return 328 } 329 if _, exist := h.vhosts[host]; exist { 330 h.next.ServeHTTP(w, r) 331 return 332 } 333 http.Error(w, "invalid host specified", http.StatusForbidden) 334 } 335 336 func newVHostHandler(vhosts []string, next http.Handler) http.Handler { 337 vhostMap := make(map[string]struct{}) 338 for _, allowedHost := range vhosts { 339 vhostMap[strings.ToLower(allowedHost)] = struct{}{} 340 } 341 return &virtualHostHandler{vhostMap, next} 342 } 343