You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

110 lines
3.2 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. // Commons for HTTP handling
  2. package rpc
  3. import (
  4. "bytes"
  5. "fmt"
  6. "net/http"
  7. "runtime/debug"
  8. "time"
  9. "github.com/tendermint/tendermint/alert"
  10. "github.com/tendermint/tendermint/binary"
  11. . "github.com/tendermint/tendermint/common"
  12. "github.com/tendermint/tendermint/config"
  13. "github.com/tendermint/tendermint/events"
  14. )
  15. func StartHTTPServer(ew *events.EventSwitch) {
  16. initHandlers(ew)
  17. log.Info(Fmt("Starting RPC HTTP server on %s", config.App().GetString("RPC.HTTP.ListenAddr")))
  18. go func() {
  19. log.Crit("RPC HTTPServer stopped", "result", http.ListenAndServe(config.App().GetString("RPC.HTTP.ListenAddr"), RecoverAndLogHandler(http.DefaultServeMux)))
  20. }()
  21. }
  22. func WriteRPCResponse(w http.ResponseWriter, res RPCResponse) {
  23. buf, n, err := new(bytes.Buffer), new(int64), new(error)
  24. binary.WriteJSON(res, buf, n, err)
  25. if *err != nil {
  26. log.Warn("Failed to write JSON RPCResponse", "error", err)
  27. }
  28. w.Header().Set("Content-Type", "application/json")
  29. w.WriteHeader(200)
  30. w.Write(buf.Bytes())
  31. }
  32. //-----------------------------------------------------------------------------
  33. // Wraps an HTTP handler, adding error logging.
  34. //
  35. // If the inner function panics, the outer function recovers, logs, sends an
  36. // HTTP 500 error response.
  37. func RecoverAndLogHandler(handler http.Handler) http.Handler {
  38. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  39. // Wrap the ResponseWriter to remember the status
  40. rww := &ResponseWriterWrapper{-1, w}
  41. begin := time.Now()
  42. // Common headers
  43. origin := r.Header.Get("Origin")
  44. rww.Header().Set("Access-Control-Allow-Origin", origin)
  45. rww.Header().Set("Access-Control-Allow-Credentials", "true")
  46. rww.Header().Set("Access-Control-Expose-Headers", "X-Server-Time")
  47. rww.Header().Set("X-Server-Time", fmt.Sprintf("%v", begin.Unix()))
  48. defer func() {
  49. // Send a 500 error if a panic happens during a handler.
  50. // Without this, Chrome & Firefox were retrying aborted ajax requests,
  51. // at least to my localhost.
  52. if e := recover(); e != nil {
  53. // If RPCResponse
  54. if res, ok := e.(RPCResponse); ok {
  55. WriteRPCResponse(rww, res)
  56. } else {
  57. // For the rest,
  58. log.Error("Panic in HTTP handler", "error", e, "stack", string(debug.Stack()))
  59. rww.WriteHeader(http.StatusInternalServerError)
  60. WriteRPCResponse(rww, NewRPCResponse(nil, Fmt("Internal Server Error: %v", e)))
  61. }
  62. }
  63. // Finally, log.
  64. durationMS := time.Since(begin).Nanoseconds() / 1000000
  65. if rww.Status == -1 {
  66. rww.Status = 200
  67. }
  68. log.Debug("Served HTTP response",
  69. "method", r.Method, "url", r.URL,
  70. "status", rww.Status, "duration", durationMS,
  71. "remoteAddr", r.RemoteAddr,
  72. )
  73. }()
  74. handler.ServeHTTP(rww, r)
  75. })
  76. }
  77. // Remember the status for logging
  78. type ResponseWriterWrapper struct {
  79. Status int
  80. http.ResponseWriter
  81. }
  82. func (w *ResponseWriterWrapper) WriteHeader(status int) {
  83. w.Status = status
  84. w.ResponseWriter.WriteHeader(status)
  85. }
  86. // Stick it as a deferred statement in gouroutines to prevent the program from crashing.
  87. func Recover(daemonName string) {
  88. if e := recover(); e != nil {
  89. stack := string(debug.Stack())
  90. errorString := fmt.Sprintf("[%s] %s\n%s", daemonName, e, stack)
  91. alert.Alert(errorString)
  92. }
  93. }