Line data Source code
1 : #include "source/common/http/status.h" 2 : 3 : #include "source/common/common/assert.h" 4 : 5 : #include "absl/strings/str_cat.h" 6 : 7 : namespace Envoy { 8 : namespace Http { 9 : 10 : namespace { 11 : 12 : constexpr absl::string_view EnvoyPayloadUrl = "Envoy"; 13 : 14 0 : absl::string_view statusCodeToString(StatusCode code) { 15 0 : switch (code) { 16 0 : case StatusCode::Ok: 17 0 : return "OK"; 18 0 : case StatusCode::CodecProtocolError: 19 0 : return "CodecProtocolError"; 20 0 : case StatusCode::BufferFloodError: 21 0 : return "BufferFloodError"; 22 0 : case StatusCode::PrematureResponseError: 23 0 : return "PrematureResponseError"; 24 0 : case StatusCode::CodecClientError: 25 0 : return "CodecClientError"; 26 0 : case StatusCode::InboundFramesWithEmptyPayload: 27 0 : return "InboundFramesWithEmptyPayloadError"; 28 0 : case StatusCode::EnvoyOverloadError: 29 0 : return "EnvoyOverloadError"; 30 0 : } 31 0 : return ""; 32 0 : } 33 : 34 : struct EnvoyStatusPayload { 35 4340 : EnvoyStatusPayload(StatusCode status_code) : status_code_(status_code) {} 36 : const StatusCode status_code_; 37 : }; 38 : 39 : struct PrematureResponsePayload : public EnvoyStatusPayload { 40 : PrematureResponsePayload(Http::Code http_code) 41 8 : : EnvoyStatusPayload(StatusCode::PrematureResponseError), http_code_(http_code) {} 42 : const Http::Code http_code_; 43 : }; 44 : 45 4340 : template <typename T> void storePayload(absl::Status& status, const T& payload) { 46 4340 : const T* allocated = new T(payload); 47 4340 : const absl::string_view sv = 48 4340 : absl::string_view(reinterpret_cast<const char*>(allocated), sizeof(allocated)); 49 4340 : absl::Cord cord = absl::MakeCordFromExternal(sv, [allocated]() { delete allocated; }); 50 4340 : cord.Flatten(); // Flatten ahead of time for easier access later. 51 4340 : status.SetPayload(EnvoyPayloadUrl, std::move(cord)); 52 4340 : } 53 : 54 751 : template <typename T = EnvoyStatusPayload> const T& getPayload(const absl::Status& status) { 55 : // The only way to get a reference to the payload owned by the absl::Status is through the 56 : // ForEachPayload method. All other methods create a copy of the payload, which is not convenient 57 : // for peeking at the payload value. 58 751 : const T* payload = nullptr; 59 751 : status.ForEachPayload([&payload](absl::string_view url, const absl::Cord& cord) { 60 751 : if (url == EnvoyPayloadUrl) { 61 751 : ASSERT(!payload); // Status API guarantees to have one payload with given URL 62 751 : auto data = cord.TryFlat(); 63 751 : ASSERT(data.has_value()); // EnvoyPayloadUrl cords are flattened ahead of time 64 751 : ASSERT(data.value().length() >= sizeof(T), "Invalid payload length"); 65 751 : payload = reinterpret_cast<const T*>(data.value().data()); 66 751 : } 67 751 : }); 68 751 : ASSERT(payload); 69 751 : return *payload; 70 751 : } 71 : 72 : } // namespace 73 : 74 0 : std::string toString(const Status& status) { 75 0 : if (status.ok()) { 76 0 : return status.ToString(); 77 0 : } 78 0 : std::string text; 79 0 : auto status_code = getStatusCode(status); 80 0 : if (status_code != StatusCode::PrematureResponseError) { 81 0 : absl::StrAppend(&text, statusCodeToString(status_code), ": ", status.message()); 82 0 : } else { 83 0 : auto http_code = getPrematureResponseHttpCode(status); 84 0 : absl::StrAppend(&text, "PrematureResponseError: HTTP code: ", http_code, ": ", 85 0 : status.message()); 86 0 : } 87 0 : return text; 88 0 : } 89 : 90 4325 : Status codecProtocolError(absl::string_view message) { 91 4325 : absl::Status status(absl::StatusCode::kInternal, message); 92 4325 : storePayload(status, EnvoyStatusPayload(StatusCode::CodecProtocolError)); 93 4325 : return status; 94 4325 : } 95 : 96 0 : Status bufferFloodError(absl::string_view message) { 97 0 : absl::Status status(absl::StatusCode::kInternal, message); 98 0 : storePayload(status, EnvoyStatusPayload(StatusCode::BufferFloodError)); 99 0 : return status; 100 0 : } 101 : 102 8 : Status prematureResponseError(absl::string_view message, Http::Code http_code) { 103 8 : absl::Status status(absl::StatusCode::kInternal, message); 104 8 : storePayload(status, PrematureResponsePayload(http_code)); 105 8 : return status; 106 8 : } 107 : 108 7 : Status codecClientError(absl::string_view message) { 109 7 : absl::Status status(absl::StatusCode::kInternal, message); 110 7 : storePayload(status, EnvoyStatusPayload(StatusCode::CodecClientError)); 111 7 : return status; 112 7 : } 113 : 114 0 : Status inboundFramesWithEmptyPayloadError() { 115 0 : absl::Status status(absl::StatusCode::kInternal, 116 0 : "Too many consecutive frames with an empty payload"); 117 0 : storePayload(status, EnvoyStatusPayload(StatusCode::InboundFramesWithEmptyPayload)); 118 0 : return status; 119 0 : } 120 : 121 0 : Status envoyOverloadError(absl::string_view message) { 122 0 : absl::Status status(absl::StatusCode::kInternal, message); 123 0 : storePayload(status, EnvoyStatusPayload(StatusCode::EnvoyOverloadError)); 124 0 : return status; 125 0 : } 126 : 127 : // Methods for checking and extracting error information 128 3970 : StatusCode getStatusCode(const Status& status) { 129 3970 : return status.ok() ? StatusCode::Ok : getPayload(status).status_code_; 130 3970 : } 131 : 132 1271 : bool isCodecProtocolError(const Status& status) { 133 1271 : return getStatusCode(status) == StatusCode::CodecProtocolError; 134 1271 : } 135 : 136 952 : bool isBufferFloodError(const Status& status) { 137 952 : return getStatusCode(status) == StatusCode::BufferFloodError; 138 952 : } 139 : 140 68 : bool isPrematureResponseError(const Status& status) { 141 68 : return getStatusCode(status) == StatusCode::PrematureResponseError; 142 68 : } 143 : 144 8 : Http::Code getPrematureResponseHttpCode(const Status& status) { 145 8 : const auto& payload = getPayload<PrematureResponsePayload>(status); 146 8 : ASSERT(payload.status_code_ == StatusCode::PrematureResponseError, 147 8 : "Must be PrematureResponseError"); 148 8 : return payload.http_code_; 149 8 : } 150 : 151 0 : bool isCodecClientError(const Status& status) { 152 0 : return getStatusCode(status) == StatusCode::CodecClientError; 153 0 : } 154 : 155 952 : bool isInboundFramesWithEmptyPayloadError(const Status& status) { 156 952 : return getStatusCode(status) == StatusCode::InboundFramesWithEmptyPayload; 157 952 : } 158 : 159 727 : bool isEnvoyOverloadError(const Status& status) { 160 727 : return getStatusCode(status) == StatusCode::EnvoyOverloadError; 161 727 : } 162 : 163 : } // namespace Http 164 : } // namespace Envoy