1 //
2 //
3 // Copyright 2016 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include "src/core/lib/transport/error_utils.h"
20
21 #include <grpc/support/port_platform.h>
22 #include <grpc/support/string_util.h>
23 #include <stdint.h>
24
25 #include <vector>
26
27 #include "src/core/lib/transport/status_conversion.h"
28 #include "src/core/util/status_helper.h"
29
recursively_find_error_with_field(grpc_error_handle error,grpc_core::StatusIntProperty which)30 static grpc_error_handle recursively_find_error_with_field(
31 grpc_error_handle error, grpc_core::StatusIntProperty which) {
32 intptr_t unused;
33 // If the error itself has a status code, return it.
34 if (grpc_error_get_int(error, which, &unused)) {
35 return error;
36 }
37 std::vector<absl::Status> children = grpc_core::StatusGetChildren(error);
38 for (const absl::Status& child : children) {
39 grpc_error_handle result = recursively_find_error_with_field(child, which);
40 if (!result.ok()) return result;
41 }
42 return absl::OkStatus();
43 }
44
grpc_error_get_status(grpc_error_handle error,grpc_core::Timestamp deadline,grpc_status_code * code,std::string * message,grpc_http2_error_code * http_error,const char ** error_string)45 void grpc_error_get_status(grpc_error_handle error,
46 grpc_core::Timestamp deadline,
47 grpc_status_code* code, std::string* message,
48 grpc_http2_error_code* http_error,
49 const char** error_string) {
50 // Fast path: We expect no error.
51 if (GPR_LIKELY(error.ok())) {
52 if (code != nullptr) *code = GRPC_STATUS_OK;
53 if (message != nullptr) {
54 // Normally, we call grpc_error_get_str(
55 // error, grpc_core::StatusStrProperty::kGrpcMessage, message).
56 // We can fastpath since we know that:
57 // 1) Error is null
58 // 2) which == grpc_core::StatusStrProperty::kGrpcMessage
59 // 3) The resulting message is statically known.
60 // 4) Said resulting message is "".
61 // This means 3 movs, instead of 10s of instructions and a strlen.
62 *message = "";
63 }
64 if (http_error != nullptr) {
65 *http_error = GRPC_HTTP2_NO_ERROR;
66 }
67 return;
68 }
69
70 // Start with the parent error and recurse through the tree of children
71 // until we find the first one that has a status code.
72 grpc_error_handle found_error = recursively_find_error_with_field(
73 error, grpc_core::StatusIntProperty::kRpcStatus);
74 if (found_error.ok()) {
75 /// If no grpc-status exists, retry through the tree to find a http2 error
76 /// code
77 found_error = recursively_find_error_with_field(
78 error, grpc_core::StatusIntProperty::kHttp2Error);
79 }
80
81 // If we found an error with a status code above, use that; otherwise,
82 // fall back to using the parent error.
83 if (found_error.ok()) found_error = error;
84
85 grpc_status_code status = GRPC_STATUS_UNKNOWN;
86 intptr_t integer;
87 if (grpc_error_get_int(found_error, grpc_core::StatusIntProperty::kRpcStatus,
88 &integer)) {
89 status = static_cast<grpc_status_code>(integer);
90 } else if (grpc_error_get_int(found_error,
91 grpc_core::StatusIntProperty::kHttp2Error,
92 &integer)) {
93 status = grpc_http2_error_to_grpc_status(
94 static_cast<grpc_http2_error_code>(integer), deadline);
95 } else {
96 status = static_cast<grpc_status_code>(found_error.code());
97 }
98 if (code != nullptr) *code = status;
99
100 if (error_string != nullptr && status != GRPC_STATUS_OK) {
101 *error_string = gpr_strdup(grpc_core::StatusToString(error).c_str());
102 }
103
104 if (http_error != nullptr) {
105 if (grpc_error_get_int(
106 found_error, grpc_core::StatusIntProperty::kHttp2Error, &integer)) {
107 *http_error = static_cast<grpc_http2_error_code>(integer);
108 } else if (grpc_error_get_int(found_error,
109 grpc_core::StatusIntProperty::kRpcStatus,
110 &integer)) {
111 *http_error =
112 grpc_status_to_http2_error(static_cast<grpc_status_code>(integer));
113 } else {
114 *http_error =
115 found_error.ok() ? GRPC_HTTP2_NO_ERROR : GRPC_HTTP2_INTERNAL_ERROR;
116 }
117 }
118
119 // If the error has a status message, use it. Otherwise, fall back to
120 // the error description.
121 if (message != nullptr) {
122 if (!grpc_error_get_str(
123 found_error, grpc_core::StatusStrProperty::kGrpcMessage, message)) {
124 if (!grpc_error_get_str(found_error,
125 grpc_core::StatusStrProperty::kDescription,
126 message)) {
127 *message = grpc_core::StatusToString(error);
128 }
129 }
130 }
131 }
132
grpc_error_to_absl_status(grpc_error_handle error)133 absl::Status grpc_error_to_absl_status(grpc_error_handle error) {
134 grpc_status_code status;
135 // TODO(yashykt): This should be updated once we decide on how to use the
136 // absl::Status payload to capture all the contents of grpc_error.
137 std::string message;
138 grpc_error_get_status(error, grpc_core::Timestamp::InfFuture(), &status,
139 &message, nullptr /* http_error */,
140 nullptr /* error_string */);
141 return absl::Status(static_cast<absl::StatusCode>(status), message);
142 }
143
absl_status_to_grpc_error(absl::Status status)144 grpc_error_handle absl_status_to_grpc_error(absl::Status status) {
145 // Special error checks
146 if (status.ok()) {
147 return absl::OkStatus();
148 }
149 return grpc_error_set_int(GRPC_ERROR_CREATE(status.message()),
150 grpc_core::StatusIntProperty::kRpcStatus,
151 static_cast<grpc_status_code>(status.code()));
152 }
153
grpc_error_has_clear_grpc_status(grpc_error_handle error)154 bool grpc_error_has_clear_grpc_status(grpc_error_handle error) {
155 intptr_t unused;
156 if (grpc_error_get_int(error, grpc_core::StatusIntProperty::kRpcStatus,
157 &unused)) {
158 return true;
159 }
160 std::vector<absl::Status> children = grpc_core::StatusGetChildren(error);
161 for (const absl::Status& child : children) {
162 if (grpc_error_has_clear_grpc_status(child)) {
163 return true;
164 }
165 }
166 return false;
167 }
168