github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/rpc/jsonrpc/server/http_server.go (about) 1 // Commons for HTTP handling 2 package server 3 4 import ( 5 "context" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "net" 10 "net/http" 11 "runtime/debug" 12 "strings" 13 "time" 14 15 "golang.org/x/net/netutil" 16 17 "github.com/ari-anchor/sei-tendermint/libs/log" 18 rpctypes "github.com/ari-anchor/sei-tendermint/rpc/jsonrpc/types" 19 ) 20 21 // Config is a RPC server configuration. 22 type Config struct { 23 // The maximum number of connections that will be accepted by the listener. 24 // See https://godoc.org/golang.org/x/net/netutil#LimitListener 25 MaxOpenConnections int 26 27 // Used to set the HTTP server's per-request read timeout. 28 // See https://godoc.org/net/http#Server.ReadTimeout 29 ReadTimeout time.Duration 30 31 // Used to set the HTTP server's per-request write timeout. Note that this 32 // affects ALL methods on the server, so it should not be set too low. This 33 // should be used as a safety valve, not a resource-control timeout. 34 // 35 // See https://godoc.org/net/http#Server.WriteTimeout 36 WriteTimeout time.Duration 37 38 // Controls the maximum number of bytes the server will read parsing the 39 // request body. 40 MaxBodyBytes int64 41 42 // Controls the maximum size of a request header. 43 // See https://godoc.org/net/http#Server.MaxHeaderBytes 44 MaxHeaderBytes int 45 } 46 47 // DefaultConfig returns a default configuration. 48 func DefaultConfig() *Config { 49 return &Config{ 50 MaxOpenConnections: 0, // unlimited 51 ReadTimeout: 10 * time.Second, 52 WriteTimeout: 0, // no default timeout 53 MaxBodyBytes: 1000000, // 1MB 54 MaxHeaderBytes: 1 << 20, // same as the net/http default 55 } 56 } 57 58 // Serve creates a http.Server and calls Serve with the given listener. It 59 // wraps handler to recover panics and limit the request body size. 60 func Serve(ctx context.Context, listener net.Listener, handler http.Handler, logger log.Logger, config *Config) error { 61 logger.Info(fmt.Sprintf("Starting RPC HTTP server on %s", listener.Addr())) 62 h := recoverAndLogHandler(MaxBytesHandler(handler, config.MaxBodyBytes), logger) 63 s := &http.Server{ 64 Handler: h, 65 ReadTimeout: config.ReadTimeout, 66 WriteTimeout: config.WriteTimeout, 67 MaxHeaderBytes: config.MaxHeaderBytes, 68 } 69 sig := make(chan struct{}) 70 go func() { 71 select { 72 case <-ctx.Done(): 73 sctx, cancel := context.WithTimeout(context.Background(), time.Second) 74 defer cancel() 75 _ = s.Shutdown(sctx) 76 case <-sig: 77 } 78 }() 79 80 if err := s.Serve(listener); err != nil { 81 logger.Info("RPC HTTP server stopped", "err", err) 82 close(sig) 83 return err 84 } 85 return nil 86 } 87 88 // Serve creates a http.Server and calls ServeTLS with the given listener, 89 // certFile and keyFile. It wraps handler to recover panics and limit the 90 // request body size. 91 func ServeTLS(ctx context.Context, listener net.Listener, handler http.Handler, certFile, keyFile string, logger log.Logger, config *Config) error { 92 logger.Info("Starting RPC HTTPS server", 93 "listenterAddr", listener.Addr(), 94 "certFile", certFile, 95 "keyFile", keyFile) 96 97 s := &http.Server{ 98 Handler: recoverAndLogHandler(MaxBytesHandler(handler, config.MaxBodyBytes), logger), 99 ReadTimeout: config.ReadTimeout, 100 WriteTimeout: config.WriteTimeout, 101 MaxHeaderBytes: config.MaxHeaderBytes, 102 } 103 sig := make(chan struct{}) 104 go func() { 105 select { 106 case <-ctx.Done(): 107 sctx, cancel := context.WithTimeout(context.Background(), time.Second) 108 defer cancel() 109 _ = s.Shutdown(sctx) 110 case <-sig: 111 } 112 }() 113 114 if err := s.ServeTLS(listener, certFile, keyFile); err != nil { 115 logger.Error("RPC HTTPS server stopped", "err", err) 116 close(sig) 117 return err 118 } 119 return nil 120 } 121 122 // writeInternalError writes an internal server error (500) to w with the text 123 // of err in the body. This is a fallback used when a handler is unable to 124 // write the expected response. 125 func writeInternalError(w http.ResponseWriter, err error) { 126 w.Header().Set("Content-Type", "text/plain") 127 w.WriteHeader(http.StatusInternalServerError) 128 fmt.Fprintln(w, err.Error()) 129 } 130 131 // writeHTTPResponse writes a JSON-RPC response to w. If rsp encodes an error, 132 // the response body is its error object; otherwise its responses is the result. 133 // 134 // Unless there is an error encoding the response, the status is 200 OK. 135 func writeHTTPResponse(w http.ResponseWriter, log log.Logger, rsp rpctypes.RPCResponse) { 136 var body []byte 137 var err error 138 if rsp.Error != nil { 139 body, err = json.Marshal(rsp.Error) 140 } else { 141 body = rsp.Result 142 } 143 if err != nil { 144 log.Error("Error encoding RPC response: %w", err) 145 writeInternalError(w, err) 146 return 147 } 148 w.Header().Set("Content-Type", "application/json") 149 w.WriteHeader(http.StatusOK) 150 _, _ = w.Write(body) 151 } 152 153 // writeRPCResponse writes one or more JSON-RPC responses to w. A single 154 // response is encoded as an object, otherwise the response is sent as a batch 155 // (array) of response objects. 156 // 157 // Unless there is an error encoding the responses, the status is 200 OK. 158 func writeRPCResponse(w http.ResponseWriter, log log.Logger, rsps ...rpctypes.RPCResponse) { 159 var body []byte 160 var err error 161 if len(rsps) == 1 { 162 body, err = json.Marshal(rsps[0]) 163 } else { 164 body, err = json.Marshal(rsps) 165 } 166 if err != nil { 167 log.Error("Error encoding RPC response: %w", err) 168 writeInternalError(w, err) 169 return 170 } 171 w.Header().Set("Content-Type", "application/json") 172 w.WriteHeader(http.StatusOK) 173 _, _ = w.Write(body) 174 } 175 176 //----------------------------------------------------------------------------- 177 178 // recoverAndLogHandler wraps an HTTP handler, adding error logging. If the 179 // inner handler panics, the wrapper recovers, logs, sends an HTTP 500 error 180 // response to the client. 181 func recoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler { 182 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 183 // Capture the HTTP status written by the handler. 184 var httpStatus int 185 rww := newStatusWriter(w, &httpStatus) 186 187 // Recover panics from inside handler and try to send the client 188 // 500 Internal server error. If the handler panicked after already 189 // sending a (partial) response, this is a no-op. 190 defer func() { 191 if v := recover(); v != nil { 192 var err error 193 switch e := v.(type) { 194 case error: 195 err = e 196 case string: 197 err = errors.New(e) 198 case fmt.Stringer: 199 err = errors.New(e.String()) 200 default: 201 err = fmt.Errorf("panic with value %v", v) 202 } 203 204 logger.Error("Panic in RPC HTTP handler", 205 "err", err, "stack", string(debug.Stack())) 206 writeInternalError(rww, err) 207 } 208 }() 209 210 // Log timing and response information from the handler. 211 begin := time.Now() 212 defer func() { 213 elapsed := time.Since(begin) 214 logger.Debug("served RPC HTTP response", 215 "method", r.Method, 216 "url", r.URL, 217 "status", httpStatus, 218 "duration-sec", elapsed.Seconds(), 219 "remoteAddr", r.RemoteAddr, 220 ) 221 }() 222 223 rww.Header().Set("X-Server-Time", fmt.Sprintf("%v", begin.Unix())) 224 handler.ServeHTTP(rww, r) 225 }) 226 } 227 228 // MaxBytesHandler wraps h in a handler that limits the size of the request 229 // body to at most maxBytes. If maxBytes <= 0, the request body is not limited. 230 func MaxBytesHandler(h http.Handler, maxBytes int64) http.Handler { 231 if maxBytes <= 0 { 232 return h 233 } 234 return maxBytesHandler{handler: h, maxBytes: maxBytes} 235 } 236 237 type maxBytesHandler struct { 238 handler http.Handler 239 maxBytes int64 240 } 241 242 func (h maxBytesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { 243 req.Body = http.MaxBytesReader(w, req.Body, h.maxBytes) 244 h.handler.ServeHTTP(w, req) 245 } 246 247 // newStatusWriter wraps an http.ResponseWriter to capture the HTTP status code 248 // in *code. 249 func newStatusWriter(w http.ResponseWriter, code *int) statusWriter { 250 return statusWriter{ 251 ResponseWriter: w, 252 Hijacker: w.(http.Hijacker), 253 code: code, 254 } 255 } 256 257 type statusWriter struct { 258 http.ResponseWriter 259 http.Hijacker // to support websocket upgrade 260 261 code *int 262 } 263 264 // WriteHeader implements part of http.ResponseWriter. It delegates to the 265 // wrapped writer, and as a side effect captures the written code. 266 // 267 // Note that if a request does not explicitly call WriteHeader, the code will 268 // not be updated. 269 func (w statusWriter) WriteHeader(code int) { 270 *w.code = code 271 w.ResponseWriter.WriteHeader(code) 272 } 273 274 // Listen starts a new net.Listener on the given address. 275 // It returns an error if the address is invalid or the call to Listen() fails. 276 func Listen(addr string, maxOpenConnections int) (listener net.Listener, err error) { 277 parts := strings.SplitN(addr, "://", 2) 278 if len(parts) != 2 { 279 return nil, fmt.Errorf( 280 "invalid listening address %s (use fully formed addresses, including the tcp:// or unix:// prefix)", 281 addr, 282 ) 283 } 284 proto, addr := parts[0], parts[1] 285 listener, err = net.Listen(proto, addr) 286 if err != nil { 287 return nil, fmt.Errorf("failed to listen on %v: %v", addr, err) 288 } 289 if maxOpenConnections > 0 { 290 listener = netutil.LimitListener(listener, maxOpenConnections) 291 } 292 293 return listener, nil 294 }