github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/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(fmt.Sprintf("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 WriteTimeout: config.WriteTimeout, 59 MaxHeaderBytes: config.MaxHeaderBytes, 60 } 61 err := s.Serve(listener) 62 logger.Info("RPC HTTP server stopped", "err", err) 63 return err 64 } 65 66 // Serve creates a http.Server and calls ServeTLS with the given listener, 67 // certFile and keyFile. It wraps handler with RecoverAndLogHandler and a 68 // handler, which limits the max body size to config.MaxBodyBytes. 69 // 70 // NOTE: This function blocks - you may want to call it in a go-routine. 71 func ServeTLS( 72 listener net.Listener, 73 handler http.Handler, 74 certFile, keyFile string, 75 logger log.Logger, 76 config *Config, 77 ) error { 78 logger.Info(fmt.Sprintf("Starting RPC HTTPS server on %s (cert: %q, key: %q)", 79 listener.Addr(), certFile, keyFile)) 80 s := &http.Server{ 81 Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: config.MaxBodyBytes}, logger), 82 ReadTimeout: config.ReadTimeout, 83 WriteTimeout: config.WriteTimeout, 84 MaxHeaderBytes: config.MaxHeaderBytes, 85 } 86 err := s.ServeTLS(listener, certFile, keyFile) 87 88 logger.Error("RPC HTTPS server stopped", "err", err) 89 return err 90 } 91 92 // WriteRPCResponseHTTPError marshals res as JSON and writes it to w. 93 // 94 // Panics if it can't Marshal res or write to w. 95 func WriteRPCResponseHTTPError( 96 w http.ResponseWriter, 97 httpCode int, 98 res types.RPCResponse, 99 ) { 100 jsonBytes, err := json.MarshalIndent(res, "", " ") 101 if err != nil { 102 panic(err) 103 } 104 105 w.Header().Set("Content-Type", "application/json") 106 w.WriteHeader(httpCode) 107 if _, err := w.Write(jsonBytes); err != nil { 108 panic(err) 109 } 110 } 111 112 // WriteRPCResponseHTTP marshals res as JSON and writes it to w. 113 // 114 // Panics if it can't Marshal res or write to w. 115 func WriteRPCResponseHTTP(w http.ResponseWriter, res ...types.RPCResponse) { 116 var v interface{} 117 if len(res) == 1 { 118 v = res[0] 119 } else { 120 v = res 121 } 122 123 jsonBytes, err := json.MarshalIndent(v, "", " ") 124 if err != nil { 125 panic(err) 126 } 127 w.Header().Set("Content-Type", "application/json") 128 w.WriteHeader(200) 129 if _, err := w.Write(jsonBytes); err != nil { 130 panic(err) 131 } 132 } 133 134 //----------------------------------------------------------------------------- 135 136 // RecoverAndLogHandler wraps an HTTP handler, adding error logging. 137 // If the inner function panics, the outer function recovers, logs, sends an 138 // HTTP 500 error response. 139 func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler { 140 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 141 // Wrap the ResponseWriter to remember the status 142 rww := &responseWriterWrapper{-1, w} 143 begin := time.Now() 144 145 rww.Header().Set("X-Server-Time", fmt.Sprintf("%v", begin.Unix())) 146 147 defer func() { 148 // Handle any panics in the panic handler below. Does not use the logger, since we want 149 // to avoid any further panics. However, we try to return a 500, since it otherwise 150 // defaults to 200 and there is no other way to terminate the connection. If that 151 // should panic for whatever reason then the Go HTTP server will handle it and 152 // terminate the connection - panicing is the de-facto and only way to get the Go HTTP 153 // server to terminate the request and close the connection/stream: 154 // https://github.com/golang/go/issues/17790#issuecomment-258481416 155 if e := recover(); e != nil { 156 fmt.Fprintf(os.Stderr, "Panic during RPC panic recovery: %v\n%v\n", e, string(debug.Stack())) 157 w.WriteHeader(500) 158 } 159 }() 160 161 defer func() { 162 // Send a 500 error if a panic happens during a handler. 163 // Without this, Chrome & Firefox were retrying aborted ajax requests, 164 // at least to my localhost. 165 if e := recover(); e != nil { 166 167 // If RPCResponse 168 if res, ok := e.(types.RPCResponse); ok { 169 WriteRPCResponseHTTP(rww, res) 170 } else { 171 // Panics can contain anything, attempt to normalize it as an error. 172 var err error 173 switch e := e.(type) { 174 case error: 175 err = e 176 case string: 177 err = errors.New(e) 178 case fmt.Stringer: 179 err = errors.New(e.String()) 180 default: 181 } 182 183 logger.Error( 184 "Panic in RPC HTTP handler", "err", e, "stack", 185 string(debug.Stack()), 186 ) 187 WriteRPCResponseHTTPError( 188 rww, 189 http.StatusInternalServerError, 190 types.RPCInternalError(types.JSONRPCIntID(-1), err), 191 ) 192 } 193 } 194 195 // Finally, log. 196 durationMS := time.Since(begin).Nanoseconds() / 1000000 197 if rww.Status == -1 { 198 rww.Status = 200 199 } 200 logger.Info("Served RPC HTTP response", 201 "method", r.Method, "url", r.URL, 202 "status", rww.Status, "duration", durationMS, 203 "remoteAddr", r.RemoteAddr, 204 ) 205 }() 206 207 handler.ServeHTTP(rww, r) 208 }) 209 } 210 211 // Remember the status for logging 212 type responseWriterWrapper struct { 213 Status int 214 http.ResponseWriter 215 } 216 217 func (w *responseWriterWrapper) WriteHeader(status int) { 218 w.Status = status 219 w.ResponseWriter.WriteHeader(status) 220 } 221 222 // implements http.Hijacker 223 func (w *responseWriterWrapper) Hijack() (net.Conn, *bufio.ReadWriter, error) { 224 return w.ResponseWriter.(http.Hijacker).Hijack() 225 } 226 227 type maxBytesHandler struct { 228 h http.Handler 229 n int64 230 } 231 232 func (h maxBytesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 233 r.Body = http.MaxBytesReader(w, r.Body, h.n) 234 h.h.ServeHTTP(w, r) 235 } 236 237 // Listen starts a new net.Listener on the given address. 238 // It returns an error if the address is invalid or the call to Listen() fails. 239 func Listen(addr string, config *Config) (listener net.Listener, err error) { 240 parts := strings.SplitN(addr, "://", 2) 241 if len(parts) != 2 { 242 return nil, fmt.Errorf( 243 "invalid listening address %s (use fully formed addresses, including the tcp:// or unix:// prefix)", 244 addr, 245 ) 246 } 247 proto, addr := parts[0], parts[1] 248 listener, err = net.Listen(proto, addr) 249 if err != nil { 250 return nil, fmt.Errorf("failed to listen on %v: %v", addr, err) 251 } 252 if config.MaxOpenConnections > 0 { 253 listener = netutil.LimitListener(listener, config.MaxOpenConnections) 254 } 255 256 return listener, nil 257 }