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 <grpc/support/port_platform.h>
20
21 #include "src/core/lib/transport/error_utils.h"
22
23 #include <grpc/support/string_util.h>
24
25 #include "src/core/lib/gprpp/status_helper.h"
26 #include "src/core/lib/iomgr/error_internal.h"
27 #include "src/core/lib/slice/slice_internal.h"
28 #include "src/core/lib/transport/status_conversion.h"
29
recursively_find_error_with_field(grpc_error_handle error,grpc_error_ints which)30 static grpc_error_handle recursively_find_error_with_field(
31 grpc_error_handle error, grpc_error_ints 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 #ifdef GRPC_ERROR_IS_ABSEIL_STATUS
38 std::vector<absl::Status> children = grpc_core::StatusGetChildren(error);
39 for (const absl::Status& child : children) {
40 grpc_error_handle result = recursively_find_error_with_field(child, which);
41 if (result != GRPC_ERROR_NONE) return result;
42 }
43 #else
44 if (grpc_error_is_special(error)) return GRPC_ERROR_NONE;
45 // Otherwise, search through its children.
46 uint8_t slot = error->first_err;
47 while (slot != UINT8_MAX) {
48 grpc_linked_error* lerr =
49 reinterpret_cast<grpc_linked_error*>(error->arena + slot);
50 grpc_error_handle result =
51 recursively_find_error_with_field(lerr->err, which);
52 if (result) return result;
53 slot = lerr->next;
54 }
55 #endif
56 return GRPC_ERROR_NONE;
57 }
58
grpc_error_get_status(grpc_error_handle error,grpc_millis deadline,grpc_status_code * code,std::string * message,grpc_http2_error_code * http_error,const char ** error_string)59 void grpc_error_get_status(grpc_error_handle error, grpc_millis deadline,
60 grpc_status_code* code, std::string* message,
61 grpc_http2_error_code* http_error,
62 const char** error_string) {
63 // Fast path: We expect no error.
64 if (GPR_LIKELY(error == GRPC_ERROR_NONE)) {
65 if (code != nullptr) *code = GRPC_STATUS_OK;
66 if (message != nullptr) {
67 // Normally, we call grpc_error_get_str(
68 // error, GRPC_ERROR_STR_GRPC_MESSAGE, message).
69 // We can fastpath since we know that:
70 // 1) Error is null
71 // 2) which == GRPC_ERROR_STR_GRPC_MESSAGE
72 // 3) The resulting message is statically known.
73 // 4) Said resulting message is "".
74 // This means 3 movs, instead of 10s of instructions and a strlen.
75 *message = "";
76 }
77 if (http_error != nullptr) {
78 *http_error = GRPC_HTTP2_NO_ERROR;
79 }
80 return;
81 }
82
83 // Start with the parent error and recurse through the tree of children
84 // until we find the first one that has a status code.
85 grpc_error_handle found_error =
86 recursively_find_error_with_field(error, GRPC_ERROR_INT_GRPC_STATUS);
87 if (found_error == GRPC_ERROR_NONE) {
88 /// If no grpc-status exists, retry through the tree to find a http2 error
89 /// code
90 found_error =
91 recursively_find_error_with_field(error, GRPC_ERROR_INT_HTTP2_ERROR);
92 }
93
94 // If we found an error with a status code above, use that; otherwise,
95 // fall back to using the parent error.
96 if (found_error == GRPC_ERROR_NONE) found_error = error;
97
98 grpc_status_code status = GRPC_STATUS_UNKNOWN;
99 intptr_t integer;
100 if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, &integer)) {
101 status = static_cast<grpc_status_code>(integer);
102 } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR,
103 &integer)) {
104 status = grpc_http2_error_to_grpc_status(
105 static_cast<grpc_http2_error_code>(integer), deadline);
106 } else {
107 #ifdef GRPC_ERROR_IS_ABSEIL_STATUS
108 status = static_cast<grpc_status_code>(found_error.code());
109 #endif
110 }
111 if (code != nullptr) *code = status;
112
113 if (error_string != nullptr && status != GRPC_STATUS_OK) {
114 *error_string = gpr_strdup(grpc_error_std_string(error).c_str());
115 }
116
117 if (http_error != nullptr) {
118 if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) {
119 *http_error = static_cast<grpc_http2_error_code>(integer);
120 } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS,
121 &integer)) {
122 *http_error =
123 grpc_status_to_http2_error(static_cast<grpc_status_code>(integer));
124 } else {
125 *http_error = found_error == GRPC_ERROR_NONE ? GRPC_HTTP2_NO_ERROR
126 : GRPC_HTTP2_INTERNAL_ERROR;
127 }
128 }
129
130 // If the error has a status message, use it. Otherwise, fall back to
131 // the error description.
132 if (message != nullptr) {
133 if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE,
134 message)) {
135 if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION,
136 message)) {
137 #ifdef GRPC_ERROR_IS_ABSEIL_STATUS
138 *message = grpc_error_std_string(error);
139 #else
140 *message = "unknown error";
141 #endif
142 }
143 }
144 }
145 }
146
grpc_error_to_absl_status(grpc_error_handle error)147 absl::Status grpc_error_to_absl_status(grpc_error_handle error) {
148 grpc_status_code status;
149 // TODO(yashykt): This should be updated once we decide on how to use the
150 // absl::Status payload to capture all the contents of grpc_error.
151 std::string message;
152 grpc_error_get_status(error, GRPC_MILLIS_INF_FUTURE, &status, &message,
153 nullptr /* http_error */, nullptr /* error_string */);
154 return absl::Status(static_cast<absl::StatusCode>(status), message);
155 }
156
absl_status_to_grpc_error(absl::Status status)157 grpc_error_handle absl_status_to_grpc_error(absl::Status status) {
158 // Special error checks
159 if (status.ok()) {
160 return GRPC_ERROR_NONE;
161 }
162 return grpc_error_set_int(
163 GRPC_ERROR_CREATE_FROM_STRING_VIEW(status.message()),
164 GRPC_ERROR_INT_GRPC_STATUS, static_cast<grpc_status_code>(status.code()));
165 }
166
grpc_error_has_clear_grpc_status(grpc_error_handle error)167 bool grpc_error_has_clear_grpc_status(grpc_error_handle error) {
168 intptr_t unused;
169 if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &unused)) {
170 return true;
171 }
172 #ifdef GRPC_ERROR_IS_ABSEIL_STATUS
173 std::vector<absl::Status> children = grpc_core::StatusGetChildren(error);
174 for (const absl::Status& child : children) {
175 if (grpc_error_has_clear_grpc_status(child)) {
176 return true;
177 }
178 }
179 #else
180 uint8_t slot = error->first_err;
181 while (slot != UINT8_MAX) {
182 grpc_linked_error* lerr =
183 reinterpret_cast<grpc_linked_error*>(error->arena + slot);
184 if (grpc_error_has_clear_grpc_status(lerr->err)) {
185 return true;
186 }
187 slot = lerr->next;
188 }
189 #endif
190 return false;
191 }
192