github.com/Oyster-zx/tendermint@v0.34.24-fork/rpc/jsonrpc/server/http_server.go (about) 1 // Commons for HTTP handling 2 package server 3 4 import ( 5 "bufio" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "net" 10 "net/http" 11 "os" 12 "runtime/debug" 13 "strings" 14 "time" 15 16 "golang.org/x/net/netutil" 17 18 "github.com/tendermint/tendermint/libs/log" 19 types "github.com/tendermint/tendermint/rpc/jsonrpc/types" 20 ) 21 22 // Config is a RPC server configuration. 23 type Config struct { 24 // see netutil.LimitListener 25 MaxOpenConnections int 26 // mirrors http.Server#ReadTimeout 27 ReadTimeout time.Duration 28 // mirrors http.Server#WriteTimeout 29 WriteTimeout time.Duration 30 // MaxBodyBytes controls the maximum number of bytes the 31 // server will read parsing the request body. 32 MaxBodyBytes int64 33 // mirrors http.Server#MaxHeaderBytes 34 MaxHeaderBytes int 35 } 36 37 // DefaultConfig returns a default configuration. 38 func DefaultConfig() *Config { 39 return &Config{ 40 MaxOpenConnections: 0, // unlimited 41 ReadTimeout: 10 * time.Second, 42 WriteTimeout: 10 * time.Second, 43 MaxBodyBytes: int64(1000000), // 1MB 44 MaxHeaderBytes: 1 << 20, // same as the net/http default 45 } 46 } 47 48 // Serve creates a http.Server and calls Serve with the given listener. It 49 // wraps handler with RecoverAndLogHandler and a handler, which limits the max 50 // body size to config.MaxBodyBytes. 51 // 52 // NOTE: This function blocks - you may want to call it in a go-routine. 53 func Serve(listener net.Listener, handler http.Handler, logger log.Logger, config *Config) error { 54 logger.Info("serve", "msg", log.NewLazySprintf("Starting RPC HTTP server on %s", listener.Addr())) 55 s := &http.Server{ 56 Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: config.MaxBodyBytes}, logger), 57 ReadTimeout: config.ReadTimeout, 58 ReadHeaderTimeout: config.ReadTimeout, 59 WriteTimeout: config.WriteTimeout, 60 MaxHeaderBytes: config.MaxHeaderBytes, 61 } 62 err := s.Serve(listener) 63 logger.Info("RPC HTTP server stopped", "err", err) 64 return err 65 } 66 67 // Serve creates a http.Server and calls ServeTLS with the given listener, 68 // certFile and keyFile. It wraps handler with RecoverAndLogHandler and a 69 // handler, which limits the max body size to config.MaxBodyBytes. 70 // 71 // NOTE: This function blocks - you may want to call it in a go-routine. 72 func ServeTLS( 73 listener net.Listener, 74 handler http.Handler, 75 certFile, keyFile string, 76 logger log.Logger, 77 config *Config, 78 ) error { 79 logger.Info("serve tls", "msg", log.NewLazySprintf("Starting RPC HTTPS server on %s (cert: %q, key: %q)", 80 listener.Addr(), certFile, keyFile)) 81 s := &http.Server{ 82 Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: config.MaxBodyBytes}, logger), 83 ReadTimeout: config.ReadTimeout, 84 ReadHeaderTimeout: config.ReadTimeout, 85 WriteTimeout: config.WriteTimeout, 86 MaxHeaderBytes: config.MaxHeaderBytes, 87 } 88 err := s.ServeTLS(listener, certFile, keyFile) 89 90 logger.Error("RPC HTTPS server stopped", "err", err) 91 return err 92 } 93 94 // WriteRPCResponseHTTPError marshals res as JSON (with indent) and writes it 95 // to w. 96 // 97 // source: https://www.jsonrpc.org/historical/json-rpc-over-http.html 98 func WriteRPCResponseHTTPError( 99 w http.ResponseWriter, 100 httpCode int, 101 res types.RPCResponse, 102 ) error { 103 if res.Error == nil { 104 panic("tried to write http error response without RPC error") 105 } 106 107 jsonBytes, err := json.Marshal(res) 108 if err != nil { 109 return fmt.Errorf("json marshal: %w", err) 110 } 111 112 w.Header().Set("Content-Type", "application/json") 113 w.WriteHeader(httpCode) 114 _, err = w.Write(jsonBytes) 115 return err 116 } 117 118 // WriteRPCResponseHTTP marshals res as JSON (with indent) and writes it to w. 119 func WriteRPCResponseHTTP(w http.ResponseWriter, res ...types.RPCResponse) error { 120 return writeRPCResponseHTTP(w, []httpHeader{}, res...) 121 } 122 123 // WriteCacheableRPCResponseHTTP marshals res as JSON (with indent) and writes 124 // it to w. Adds cache-control to the response header and sets the expiry to 125 // one day. 126 func WriteCacheableRPCResponseHTTP(w http.ResponseWriter, res ...types.RPCResponse) error { 127 return writeRPCResponseHTTP(w, []httpHeader{{"Cache-Control", "public, max-age=86400"}}, res...) 128 } 129 130 type httpHeader struct { 131 name string 132 value string 133 } 134 135 func writeRPCResponseHTTP(w http.ResponseWriter, headers []httpHeader, res ...types.RPCResponse) error { 136 var v interface{} 137 if len(res) == 1 { 138 v = res[0] 139 } else { 140 v = res 141 } 142 143 jsonBytes, err := json.Marshal(v) 144 if err != nil { 145 return fmt.Errorf("json marshal: %w", err) 146 } 147 w.Header().Set("Content-Type", "application/json") 148 for _, header := range headers { 149 w.Header().Set(header.name, header.value) 150 } 151 w.WriteHeader(200) 152 _, err = w.Write(jsonBytes) 153 return err 154 } 155 156 //----------------------------------------------------------------------------- 157 158 // RecoverAndLogHandler wraps an HTTP handler, adding error logging. 159 // If the inner function panics, the outer function recovers, logs, sends an 160 // HTTP 500 error response. 161 func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler { 162 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 163 // Wrap the ResponseWriter to remember the status 164 rww := &responseWriterWrapper{-1, w} 165 begin := time.Now() 166 167 rww.Header().Set("X-Server-Time", fmt.Sprintf("%v", begin.Unix())) 168 169 defer func() { 170 // Handle any panics in the panic handler below. Does not use the logger, since we want 171 // to avoid any further panics. However, we try to return a 500, since it otherwise 172 // defaults to 200 and there is no other way to terminate the connection. If that 173 // should panic for whatever reason then the Go HTTP server will handle it and 174 // terminate the connection - panicing is the de-facto and only way to get the Go HTTP 175 // server to terminate the request and close the connection/stream: 176 // https://github.com/golang/go/issues/17790#issuecomment-258481416 177 if e := recover(); e != nil { 178 fmt.Fprintf(os.Stderr, "Panic during RPC panic recovery: %v\n%v\n", e, string(debug.Stack())) 179 w.WriteHeader(500) 180 } 181 }() 182 183 defer func() { 184 // Send a 500 error if a panic happens during a handler. 185 // Without this, Chrome & Firefox were retrying aborted ajax requests, 186 // at least to my localhost. 187 if e := recover(); e != nil { 188 // If RPCResponse 189 if res, ok := e.(types.RPCResponse); ok { 190 if wErr := WriteRPCResponseHTTP(rww, res); wErr != nil { 191 logger.Error("failed to write response", "res", res, "err", wErr) 192 } 193 } else { 194 // Panics can contain anything, attempt to normalize it as an error. 195 var err error 196 switch e := e.(type) { 197 case error: 198 err = e 199 case string: 200 err = errors.New(e) 201 case fmt.Stringer: 202 err = errors.New(e.String()) 203 default: 204 } 205 206 logger.Error("panic in RPC HTTP handler", "err", e, "stack", string(debug.Stack())) 207 208 res := types.RPCInternalError(types.JSONRPCIntID(-1), err) 209 if wErr := WriteRPCResponseHTTPError(rww, http.StatusInternalServerError, res); wErr != nil { 210 logger.Error("failed to write response", "res", res, "err", wErr) 211 } 212 } 213 } 214 215 // Finally, log. 216 durationMS := time.Since(begin).Nanoseconds() / 1000000 217 if rww.Status == -1 { 218 rww.Status = 200 219 } 220 logger.Debug("served RPC HTTP response", 221 "method", r.Method, 222 "url", r.URL, 223 "status", rww.Status, 224 "duration", durationMS, 225 "remoteAddr", r.RemoteAddr, 226 ) 227 }() 228 229 handler.ServeHTTP(rww, r) 230 }) 231 } 232 233 // Remember the status for logging 234 type responseWriterWrapper struct { 235 Status int 236 http.ResponseWriter 237 } 238 239 func (w *responseWriterWrapper) WriteHeader(status int) { 240 w.Status = status 241 w.ResponseWriter.WriteHeader(status) 242 } 243 244 // implements http.Hijacker 245 func (w *responseWriterWrapper) Hijack() (net.Conn, *bufio.ReadWriter, error) { 246 return w.ResponseWriter.(http.Hijacker).Hijack() 247 } 248 249 type maxBytesHandler struct { 250 h http.Handler 251 n int64 252 } 253 254 func (h maxBytesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 255 r.Body = http.MaxBytesReader(w, r.Body, h.n) 256 h.h.ServeHTTP(w, r) 257 } 258 259 // Listen starts a new net.Listener on the given address. 260 // It returns an error if the address is invalid or the call to Listen() fails. 261 func Listen(addr string, config *Config) (listener net.Listener, err error) { 262 parts := strings.SplitN(addr, "://", 2) 263 if len(parts) != 2 { 264 return nil, fmt.Errorf( 265 "invalid listening address %s (use fully formed addresses, including the tcp:// or unix:// prefix)", 266 addr, 267 ) 268 } 269 proto, addr := parts[0], parts[1] 270 listener, err = net.Listen(proto, addr) 271 if err != nil { 272 return nil, fmt.Errorf("failed to listen on %v: %v", addr, err) 273 } 274 if config.MaxOpenConnections > 0 { 275 listener = netutil.LimitListener(listener, config.MaxOpenConnections) 276 } 277 278 return listener, nil 279 }