summaryrefslogtreecommitdiff
path: root/grpc/src/core/lib/transport/error_utils.cc
blob: c6ef8ae636b4be3fcf2d9bfe46cef2886ceb599f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 *
 * Copyright 2016 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <grpc/support/port_platform.h>

#include "src/core/lib/transport/error_utils.h"

#include <grpc/support/string_util.h>
#include "src/core/lib/iomgr/error_internal.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/transport/status_conversion.h"

static grpc_error* recursively_find_error_with_field(grpc_error* error,
                                                     grpc_error_ints which) {
  intptr_t unused;
  // If the error itself has a status code, return it.
  if (grpc_error_get_int(error, which, &unused)) {
    return error;
  }
  if (grpc_error_is_special(error)) return nullptr;
  // Otherwise, search through its children.
  uint8_t slot = error->first_err;
  while (slot != UINT8_MAX) {
    grpc_linked_error* lerr =
        reinterpret_cast<grpc_linked_error*>(error->arena + slot);
    grpc_error* result = recursively_find_error_with_field(lerr->err, which);
    if (result) return result;
    slot = lerr->next;
  }
  return nullptr;
}

void grpc_error_get_status(grpc_error* error, grpc_millis deadline,
                           grpc_status_code* code, grpc_slice* slice,
                           grpc_http2_error_code* http_error,
                           const char** error_string) {
  // Fast path: We expect no error.
  if (GPR_LIKELY(error == GRPC_ERROR_NONE)) {
    if (code != nullptr) *code = GRPC_STATUS_OK;
    if (slice != nullptr) {
      // Normally, we call grpc_error_get_str(
      //   error, GRPC_ERROR_STR_GRPC_MESSAGE, slice).
      // We can fastpath since we know that:
      // 1) Error is null
      // 2) which == GRPC_ERROR_STR_GRPC_MESSAGE
      // 3) The resulting slice is statically known.
      // 4) Said resulting slice is of length 0 ("").
      // This means 3 movs, instead of 10s of instructions and a strlen.
      *slice = grpc_core::ExternallyManagedSlice("");
    }
    if (http_error != nullptr) {
      *http_error = GRPC_HTTP2_NO_ERROR;
    }
    return;
  }

  // Start with the parent error and recurse through the tree of children
  // until we find the first one that has a status code.
  grpc_error* found_error =
      recursively_find_error_with_field(error, GRPC_ERROR_INT_GRPC_STATUS);
  if (found_error == nullptr) {
    /// If no grpc-status exists, retry through the tree to find a http2 error
    /// code
    found_error =
        recursively_find_error_with_field(error, GRPC_ERROR_INT_HTTP2_ERROR);
  }

  // If we found an error with a status code above, use that; otherwise,
  // fall back to using the parent error.
  if (found_error == nullptr) found_error = error;

  grpc_status_code status = GRPC_STATUS_UNKNOWN;
  intptr_t integer;
  if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, &integer)) {
    status = static_cast<grpc_status_code>(integer);
  } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR,
                                &integer)) {
    status = grpc_http2_error_to_grpc_status(
        static_cast<grpc_http2_error_code>(integer), deadline);
  }
  if (code != nullptr) *code = status;

  if (error_string != nullptr && status != GRPC_STATUS_OK) {
    *error_string = gpr_strdup(grpc_error_string(error));
  }

  if (http_error != nullptr) {
    if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) {
      *http_error = static_cast<grpc_http2_error_code>(integer);
    } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS,
                                  &integer)) {
      *http_error =
          grpc_status_to_http2_error(static_cast<grpc_status_code>(integer));
    } else {
      *http_error = found_error == GRPC_ERROR_NONE ? GRPC_HTTP2_NO_ERROR
                                                   : GRPC_HTTP2_INTERNAL_ERROR;
    }
  }

  // If the error has a status message, use it.  Otherwise, fall back to
  // the error description.
  if (slice != nullptr) {
    if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE, slice)) {
      if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION, slice)) {
        *slice = grpc_slice_from_static_string("unknown error");
      }
    }
  }
}

absl::Status grpc_error_to_absl_status(grpc_error* error) {
  grpc_status_code status;
  // TODO(yashykt): This should be updated once we decide on how to use the
  // absl::Status payload to capture all the contents of grpc_error.
  grpc_slice message;
  grpc_error_get_status(error, GRPC_MILLIS_INF_FUTURE, &status, &message,
                        nullptr /* http_error */, nullptr /* error_string */);
  return absl::Status(static_cast<absl::StatusCode>(status),
                      absl::string_view(reinterpret_cast<const char*>(
                                            GRPC_SLICE_START_PTR(message)),
                                        GRPC_SLICE_LENGTH(message)));
}

bool grpc_error_has_clear_grpc_status(grpc_error* error) {
  intptr_t unused;
  if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &unused)) {
    return true;
  }
  uint8_t slot = error->first_err;
  while (slot != UINT8_MAX) {
    grpc_linked_error* lerr =
        reinterpret_cast<grpc_linked_error*>(error->arena + slot);
    if (grpc_error_has_clear_grpc_status(lerr->err)) {
      return true;
    }
    slot = lerr->next;
  }
  return false;
}