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
5
absl::string_view statusCodeToString(StatusCode code) {
15
5
  switch (code) {
16
  case StatusCode::Ok:
17
    return "OK";
18
1
  case StatusCode::CodecProtocolError:
19
1
    return "CodecProtocolError";
20
1
  case StatusCode::BufferFloodError:
21
1
    return "BufferFloodError";
22
  case StatusCode::PrematureResponseError:
23
    return "PrematureResponseError";
24
1
  case StatusCode::CodecClientError:
25
1
    return "CodecClientError";
26
1
  case StatusCode::InboundFramesWithEmptyPayload:
27
1
    return "InboundFramesWithEmptyPayloadError";
28
1
  case StatusCode::EnvoyOverloadError:
29
1
    return "EnvoyOverloadError";
30
  case StatusCode::GoAwayGracefulClose:
31
    return "GoAwayGracefulClose";
32
5
  }
33
  return "";
34
5
}
35

            
36
struct EnvoyStatusPayload {
37
3083
  EnvoyStatusPayload(StatusCode status_code) : status_code_(status_code) {}
38
  const StatusCode status_code_;
39
};
40

            
41
struct PrematureResponsePayload : public EnvoyStatusPayload {
42
  PrematureResponsePayload(Http::Code http_code)
43
7
      : EnvoyStatusPayload(StatusCode::PrematureResponseError), http_code_(http_code) {}
44
  const Http::Code http_code_;
45
};
46

            
47
3083
template <typename T> void storePayload(absl::Status& status, const T& payload) {
48
3083
  const T* allocated = new T(payload);
49
3083
  const absl::string_view sv =
50
3083
      absl::string_view(reinterpret_cast<const char*>(allocated), sizeof(T));
51
3083
  absl::Cord cord = absl::MakeCordFromExternal(sv, [allocated]() { delete allocated; });
52
3083
  cord.Flatten(); // Flatten ahead of time for easier access later.
53
3083
  status.SetPayload(EnvoyPayloadUrl, std::move(cord));
54
3083
}
55

            
56
7101
template <typename T = EnvoyStatusPayload> const T& getPayload(const absl::Status& status) {
57
  // The only way to get a reference to the payload owned by the absl::Status is through the
58
  // ForEachPayload method. All other methods create a copy of the payload, which is not convenient
59
  // for peeking at the payload value.
60
7101
  const T* payload = nullptr;
61
7101
  status.ForEachPayload([&payload](absl::string_view url, const absl::Cord& cord) {
62
7101
    if (url == EnvoyPayloadUrl) {
63
7101
      ASSERT(!payload); // Status API guarantees to have one payload with given URL
64
7101
      auto data = cord.TryFlat();
65
7101
      ASSERT(data.has_value()); // EnvoyPayloadUrl cords are flattened ahead of time
66
7101
      ASSERT(data.value().length() >= sizeof(T), "Invalid payload length");
67
7101
      payload = reinterpret_cast<const T*>(data.value().data());
68
7101
    }
69
7101
  });
70
7101
  ASSERT(payload);
71
7101
  return *payload;
72
7101
}
73

            
74
} // namespace
75

            
76
7
std::string toString(const Status& status) {
77
7
  if (status.ok()) {
78
1
    return status.ToString();
79
1
  }
80
6
  std::string text;
81
6
  auto status_code = getStatusCode(status);
82
6
  if (status_code != StatusCode::PrematureResponseError) {
83
5
    absl::StrAppend(&text, statusCodeToString(status_code), ": ", status.message());
84
5
  } else {
85
1
    auto http_code = getPrematureResponseHttpCode(status);
86
1
    absl::StrAppend(&text, "PrematureResponseError: HTTP code: ", http_code, ": ",
87
1
                    status.message());
88
1
  }
89
6
  return text;
90
7
}
91

            
92
2903
Status codecProtocolError(absl::string_view message) {
93
2903
  absl::Status status(absl::StatusCode::kInternal, message);
94
2903
  storePayload(status, EnvoyStatusPayload(StatusCode::CodecProtocolError));
95
2903
  return status;
96
2903
}
97

            
98
110
Status bufferFloodError(absl::string_view message) {
99
110
  absl::Status status(absl::StatusCode::kInternal, message);
100
110
  storePayload(status, EnvoyStatusPayload(StatusCode::BufferFloodError));
101
110
  return status;
102
110
}
103

            
104
7
Status prematureResponseError(absl::string_view message, Http::Code http_code) {
105
7
  absl::Status status(absl::StatusCode::kInternal, message);
106
7
  storePayload(status, PrematureResponsePayload(http_code));
107
7
  return status;
108
7
}
109

            
110
5
Status codecClientError(absl::string_view message) {
111
5
  absl::Status status(absl::StatusCode::kInternal, message);
112
5
  storePayload(status, EnvoyStatusPayload(StatusCode::CodecClientError));
113
5
  return status;
114
5
}
115

            
116
19
Status inboundFramesWithEmptyPayloadError() {
117
19
  absl::Status status(absl::StatusCode::kInternal,
118
19
                      "Too many consecutive frames with an empty payload");
119
19
  storePayload(status, EnvoyStatusPayload(StatusCode::InboundFramesWithEmptyPayload));
120
19
  return status;
121
19
}
122

            
123
12
Status envoyOverloadError(absl::string_view message) {
124
12
  absl::Status status(absl::StatusCode::kInternal, message);
125
12
  storePayload(status, EnvoyStatusPayload(StatusCode::EnvoyOverloadError));
126
12
  return status;
127
12
}
128

            
129
27
Status goAwayGracefulCloseError() {
130
27
  absl::Status status(absl::StatusCode::kInternal, {});
131
27
  storePayload(status, EnvoyStatusPayload(StatusCode::GoAwayGracefulClose));
132
27
  return status;
133
27
}
134

            
135
// Methods for checking and extracting error information
136
524789
StatusCode getStatusCode(const Status& status) {
137
440994
  return status.ok() ? StatusCode::Ok : getPayload(status).status_code_;
138
524789
}
139

            
140
316538
bool isCodecProtocolError(const Status& status) {
141
316538
  return getStatusCode(status) == StatusCode::CodecProtocolError;
142
316538
}
143

            
144
69592
bool isBufferFloodError(const Status& status) {
145
69592
  return getStatusCode(status) == StatusCode::BufferFloodError;
146
69592
}
147

            
148
616
bool isPrematureResponseError(const Status& status) {
149
616
  return getStatusCode(status) == StatusCode::PrematureResponseError;
150
616
}
151

            
152
5
Http::Code getPrematureResponseHttpCode(const Status& status) {
153
5
  const auto& payload = getPayload<PrematureResponsePayload>(status);
154
5
  ASSERT(payload.status_code_ == StatusCode::PrematureResponseError,
155
5
         "Must be PrematureResponseError");
156
5
  return payload.http_code_;
157
5
}
158

            
159
9
bool isCodecClientError(const Status& status) {
160
9
  return getStatusCode(status) == StatusCode::CodecClientError;
161
9
}
162

            
163
69588
bool isInboundFramesWithEmptyPayloadError(const Status& status) {
164
69588
  return getStatusCode(status) == StatusCode::InboundFramesWithEmptyPayload;
165
69588
}
166

            
167
67832
bool isEnvoyOverloadError(const Status& status) {
168
67832
  return getStatusCode(status) == StatusCode::EnvoyOverloadError;
169
67832
}
170

            
171
605
bool isGoAwayGracefulCloseError(const Status& status) {
172
605
  return getStatusCode(status) == StatusCode::GoAwayGracefulClose;
173
605
}
174

            
175
} // namespace Http
176
} // namespace Envoy