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.

156 lines
4.5 KiB

8 years ago
8 years ago
8 years ago
7 years ago
7 years ago
8 years ago
8 years ago
  1. // Commons for HTTP handling
  2. package rpcserver
  3. import (
  4. "bufio"
  5. "encoding/json"
  6. "fmt"
  7. "net"
  8. "net/http"
  9. "runtime/debug"
  10. "strings"
  11. "time"
  12. "github.com/pkg/errors"
  13. types "github.com/tendermint/tendermint/rpc/lib/types"
  14. "github.com/tendermint/tmlibs/log"
  15. )
  16. func StartHTTPServer(listenAddr string, handler http.Handler, logger log.Logger) (listener net.Listener, err error) {
  17. var proto, addr string
  18. parts := strings.SplitN(listenAddr, "://", 2)
  19. if len(parts) != 2 {
  20. return nil, errors.Errorf("Invalid listening address %s (use fully formed addresses, including the tcp:// or unix:// prefix)", listenAddr)
  21. }
  22. proto, addr = parts[0], parts[1]
  23. logger.Info(fmt.Sprintf("Starting RPC HTTP server on %s", listenAddr))
  24. listener, err = net.Listen(proto, addr)
  25. if err != nil {
  26. return nil, errors.Errorf("Failed to listen on %v: %v", listenAddr, err)
  27. }
  28. go func() {
  29. err := http.Serve(
  30. listener,
  31. RecoverAndLogHandler(handler, logger),
  32. )
  33. logger.Error("RPC HTTP server stopped", "err", err)
  34. }()
  35. return listener, nil
  36. }
  37. func StartHTTPAndTLSServer(listenAddr string, handler http.Handler, certFile, keyFile string, logger log.Logger) (listener net.Listener, err error) {
  38. var proto, addr string
  39. parts := strings.SplitN(listenAddr, "://", 2)
  40. if len(parts) != 2 {
  41. return nil, errors.Errorf("Invalid listening address %s (use fully formed addresses, including the tcp:// or unix:// prefix)", listenAddr)
  42. }
  43. proto, addr = parts[0], parts[1]
  44. logger.Info(fmt.Sprintf("Starting RPC HTTPS server on %s (cert: %q, key: %q)", listenAddr, certFile, keyFile))
  45. listener, err = net.Listen(proto, addr)
  46. if err != nil {
  47. return nil, errors.Errorf("Failed to listen on %v: %v", listenAddr, err)
  48. }
  49. go func() {
  50. err := http.ServeTLS(
  51. listener,
  52. RecoverAndLogHandler(handler, logger),
  53. certFile,
  54. keyFile,
  55. )
  56. logger.Error("RPC HTTPS server stopped", "err", err)
  57. }()
  58. return listener, nil
  59. }
  60. func WriteRPCResponseHTTPError(w http.ResponseWriter, httpCode int, res types.RPCResponse) {
  61. jsonBytes, err := json.MarshalIndent(res, "", " ")
  62. if err != nil {
  63. panic(err)
  64. }
  65. w.Header().Set("Content-Type", "application/json")
  66. w.WriteHeader(httpCode)
  67. w.Write(jsonBytes) // nolint: errcheck, gas
  68. }
  69. func WriteRPCResponseHTTP(w http.ResponseWriter, res types.RPCResponse) {
  70. jsonBytes, err := json.MarshalIndent(res, "", " ")
  71. if err != nil {
  72. panic(err)
  73. }
  74. w.Header().Set("Content-Type", "application/json")
  75. w.WriteHeader(200)
  76. w.Write(jsonBytes) // nolint: errcheck, gas
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Wraps an HTTP handler, adding error logging.
  80. // If the inner function panics, the outer function recovers, logs, sends an
  81. // HTTP 500 error response.
  82. func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler {
  83. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  84. // Wrap the ResponseWriter to remember the status
  85. rww := &ResponseWriterWrapper{-1, w}
  86. begin := time.Now()
  87. // Common headers
  88. origin := r.Header.Get("Origin")
  89. rww.Header().Set("Access-Control-Allow-Origin", origin)
  90. rww.Header().Set("Access-Control-Allow-Credentials", "true")
  91. rww.Header().Set("Access-Control-Expose-Headers", "X-Server-Time")
  92. rww.Header().Set("X-Server-Time", fmt.Sprintf("%v", begin.Unix()))
  93. defer func() {
  94. // Send a 500 error if a panic happens during a handler.
  95. // Without this, Chrome & Firefox were retrying aborted ajax requests,
  96. // at least to my localhost.
  97. if e := recover(); e != nil {
  98. // If RPCResponse
  99. if res, ok := e.(types.RPCResponse); ok {
  100. WriteRPCResponseHTTP(rww, res)
  101. } else {
  102. // For the rest,
  103. logger.Error("Panic in RPC HTTP handler", "err", e, "stack", string(debug.Stack()))
  104. rww.WriteHeader(http.StatusInternalServerError)
  105. WriteRPCResponseHTTP(rww, types.RPCInternalError("", e.(error)))
  106. }
  107. }
  108. // Finally, log.
  109. durationMS := time.Since(begin).Nanoseconds() / 1000000
  110. if rww.Status == -1 {
  111. rww.Status = 200
  112. }
  113. logger.Info("Served RPC HTTP response",
  114. "method", r.Method, "url", r.URL,
  115. "status", rww.Status, "duration", durationMS,
  116. "remoteAddr", r.RemoteAddr,
  117. )
  118. }()
  119. handler.ServeHTTP(rww, r)
  120. })
  121. }
  122. // Remember the status for logging
  123. type ResponseWriterWrapper struct {
  124. Status int
  125. http.ResponseWriter
  126. }
  127. func (w *ResponseWriterWrapper) WriteHeader(status int) {
  128. w.Status = status
  129. w.ResponseWriter.WriteHeader(status)
  130. }
  131. // implements http.Hijacker
  132. func (w *ResponseWriterWrapper) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  133. return w.ResponseWriter.(http.Hijacker).Hijack()
  134. }