http.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package proto
  2. import (
  3. "fmt"
  4. "net/http"
  5. "github.com/opencost/opencost/pkg/util/json"
  6. "google.golang.org/protobuf/encoding/protojson"
  7. "google.golang.org/protobuf/proto"
  8. )
  9. // HTTPProtocol is a struct used as a selector for request/response protocol utility methods
  10. type HTTPProtocol struct{}
  11. // HTTPError represents an http error response
  12. type HTTPError struct {
  13. StatusCode int
  14. Body string
  15. }
  16. // Error returns the error string
  17. func (he HTTPError) Error() string {
  18. return string(he.Body)
  19. }
  20. // BadRequest creates a BadRequest HTTPError
  21. func (hp HTTPProtocol) BadRequest(message string) HTTPError {
  22. return HTTPError{
  23. StatusCode: http.StatusBadRequest,
  24. Body: message,
  25. }
  26. }
  27. // InternalServerError creates an InternalServerError HTTPError
  28. func (hp HTTPProtocol) InternalServerError(message string) HTTPError {
  29. if message == "" {
  30. message = "Internal Server Error"
  31. }
  32. return HTTPError{
  33. StatusCode: http.StatusInternalServerError,
  34. Body: message,
  35. }
  36. }
  37. // NotFound creates a NotFound HTTPError
  38. func (hp HTTPProtocol) NotFound() HTTPError {
  39. return HTTPError{
  40. StatusCode: http.StatusNotFound,
  41. Body: "Not Found",
  42. }
  43. }
  44. // HTTPResponse represents a data envelope for our HTTP messaging
  45. type HTTPResponse struct {
  46. Code int `json:"code"`
  47. Data interface{} `json:"data"`
  48. Message string `json:"message,omitempty"`
  49. Warning string `json:"warning,omitempty"`
  50. }
  51. // ToResponse accepts a data payload and/or error to encode into a new HTTPResponse instance. Responses
  52. // which should not contain an error should pass nil for err.
  53. func (hp HTTPProtocol) ToResponse(data interface{}, err error) *HTTPResponse {
  54. if err != nil {
  55. return &HTTPResponse{
  56. Code: http.StatusInternalServerError,
  57. Data: data,
  58. Message: err.Error(),
  59. }
  60. }
  61. return &HTTPResponse{
  62. Code: http.StatusOK,
  63. Data: data,
  64. }
  65. }
  66. // WriteData wraps the data payload in an HTTPResponse and writes the resulting response using the
  67. // http.ResponseWriter
  68. func (hp HTTPProtocol) WriteData(w http.ResponseWriter, data interface{}) {
  69. status := http.StatusOK
  70. resp, err := json.Marshal(&HTTPResponse{
  71. Code: status,
  72. Data: data,
  73. })
  74. if err != nil {
  75. status = http.StatusInternalServerError
  76. resp, _ = json.Marshal(&HTTPResponse{
  77. Code: status,
  78. Message: fmt.Sprintf("Error: %s", err),
  79. })
  80. }
  81. w.WriteHeader(status)
  82. w.Write(resp)
  83. }
  84. // WriteDataWithWarning writes the data payload similiar to WriteData except it provides an additional warning message.
  85. func (hp HTTPProtocol) WriteDataWithWarning(w http.ResponseWriter, data interface{}, warning string) {
  86. status := http.StatusOK
  87. resp, err := json.Marshal(&HTTPResponse{
  88. Code: status,
  89. Data: data,
  90. Warning: warning,
  91. })
  92. if err != nil {
  93. status = http.StatusInternalServerError
  94. resp, _ = json.Marshal(&HTTPResponse{
  95. Code: status,
  96. Message: fmt.Sprintf("Error: %s", err),
  97. })
  98. }
  99. w.WriteHeader(status)
  100. w.Write(resp)
  101. }
  102. // WriteDataWithMessage writes the data payload similiar to WriteData except it provides an additional string message.
  103. func (hp HTTPProtocol) WriteDataWithMessage(w http.ResponseWriter, data interface{}, message string) {
  104. status := http.StatusOK
  105. resp, err := json.Marshal(&HTTPResponse{
  106. Code: status,
  107. Data: data,
  108. Message: message,
  109. })
  110. if err != nil {
  111. status = http.StatusInternalServerError
  112. resp, _ = json.Marshal(&HTTPResponse{
  113. Code: status,
  114. Message: fmt.Sprintf("Error: %s", err),
  115. })
  116. }
  117. w.WriteHeader(status)
  118. w.Write(resp)
  119. }
  120. // WriteProtoWithMessage uses the protojson package to convert proto3 response to json response and
  121. // return it to the requester. Proto3 drops messages with default values but overriding the param
  122. // EmitUnpopulated to true it returns default values in the Json response payload. If error is
  123. // encountered it sent InternalServerError and the error why the json conversion failed.
  124. func (hp HTTPProtocol) WriteProtoWithMessage(w http.ResponseWriter, data proto.Message) {
  125. m := protojson.MarshalOptions{
  126. EmitUnpopulated: true,
  127. }
  128. status := http.StatusOK
  129. resp, err := m.Marshal(data)
  130. if err != nil {
  131. status = http.StatusInternalServerError
  132. resp, _ = json.Marshal(&HTTPResponse{
  133. Message: fmt.Sprintf("Error: %s", err),
  134. })
  135. }
  136. w.WriteHeader(status)
  137. w.Write(resp)
  138. }
  139. // WriteDataWithMessageAndWarning writes the data payload similiar to WriteData except it provides a warning and additional message string.
  140. func (hp HTTPProtocol) WriteDataWithMessageAndWarning(w http.ResponseWriter, data interface{}, message string, warning string) {
  141. status := http.StatusOK
  142. resp, err := json.Marshal(&HTTPResponse{
  143. Code: status,
  144. Data: data,
  145. Message: message,
  146. Warning: warning,
  147. })
  148. if err != nil {
  149. status = http.StatusInternalServerError
  150. resp, _ = json.Marshal(&HTTPResponse{
  151. Code: status,
  152. Message: fmt.Sprintf("Error: %s", err),
  153. })
  154. }
  155. w.WriteHeader(status)
  156. w.Write(resp)
  157. }
  158. // WriteError wraps the HTTPError in a HTTPResponse and writes it via http.ResponseWriter
  159. func (hp HTTPProtocol) WriteError(w http.ResponseWriter, err HTTPError) {
  160. status := err.StatusCode
  161. if status == 0 {
  162. status = http.StatusInternalServerError
  163. }
  164. w.WriteHeader(status)
  165. resp, _ := json.Marshal(&HTTPResponse{
  166. Code: status,
  167. Message: err.Body,
  168. })
  169. w.Write(resp)
  170. }
  171. // WriteResponse writes the provided HTTPResponse instance via http.ResponseWriter
  172. func (hp HTTPProtocol) WriteResponse(w http.ResponseWriter, r *HTTPResponse) {
  173. status := r.Code
  174. resp, err := json.Marshal(r)
  175. if err != nil {
  176. status = http.StatusInternalServerError
  177. resp, _ = json.Marshal(&HTTPResponse{
  178. Code: status,
  179. Message: fmt.Sprintf("Error: %s", err),
  180. })
  181. }
  182. w.WriteHeader(status)
  183. w.Write(resp)
  184. }