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