2
0

http.go 5.9 KB

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