• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 2213

10 Apr 2024 11:21PM UTC coverage: 91.792% (-0.8%) from 92.623%
2213

push

Evergreen

web-flow
Add missing availability checks for SecCopyErrorMessageString (#7577)

This requires iOS 11.3 and we currently target iOS 11.

94842 of 175770 branches covered (53.96%)

7 of 22 new or added lines in 2 files covered. (31.82%)

1861 existing lines in 82 files now uncovered.

242866 of 264583 relevant lines covered (91.79%)

5593111.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.6
/src/realm/object-store/sync/app_utils.cpp
1
////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2020 Realm Inc.
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 utilied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
//
17
////////////////////////////////////////////////////////////////////////////
18

19
#include <realm/object-store/sync/app_utils.hpp>
20

21
#include <realm/object-store/sync/generic_network_transport.hpp>
22
#include <realm/sync/network/http.hpp>
23
#include <realm/util/uri.hpp>
24

25
#include <external/json/json.hpp>
26

27
#include <algorithm>
28

29
namespace realm::app {
30

31
const std::pair<const std::string, std::string>*
32
AppUtils::find_header(const std::string& key_name, const std::map<std::string, std::string>& search_map)
33
{
36,541✔
34
    for (auto&& current : search_map) {
41,767✔
35
#ifdef _MSC_VER
36
        if (key_name.size() == current.first.size() && _stricmp(key_name.c_str(), current.first.c_str()) == 0) {
37
            return &current;
38
        }
39
#else
40
        if (key_name.size() == current.first.size() && strcasecmp(key_name.c_str(), current.first.c_str()) == 0) {
41,767✔
41
            return &current;
17,855✔
42
        }
17,855✔
43
#endif
41,767✔
44
    }
41,767✔
45
    return nullptr;
27,387✔
46
}
36,541✔
47

48
bool AppUtils::is_success_status_code(int status_code)
49
{
25,143✔
50
    return status_code == 0 || (status_code < 300 && status_code >= 200);
25,143✔
51
}
25,143✔
52

53
bool AppUtils::is_redirect_status_code(int status_code)
54
{
19,307✔
55
    using namespace realm::sync;
19,307✔
56
    // If the response contains a redirection, then return true
9,524✔
57
    if (auto code = HTTPStatus(status_code);
19,307✔
58
        code == HTTPStatus::MovedPermanently || code == HTTPStatus::PermanentRedirect) {
19,307✔
59
        return true;
118✔
60
    }
118✔
61
    return false;
19,189✔
62
}
19,189✔
63

64
std::optional<std::string> AppUtils::extract_redir_location(const std::map<std::string, std::string>& headers)
65
{
128✔
66
    // Look for case insensitive redirect "location" in headers
64✔
67
    auto location = AppUtils::find_header("location", headers);
128✔
68
    if (location && !location->second.empty() && util::Uri::try_parse(location->second).is_ok()) {
128✔
69
        // If the location is valid, return it wholesale (e.g., it could include a path for API proxies)
57✔
70
        return location->second;
114✔
71
    }
114✔
72
    return std::nullopt;
14✔
73
}
14✔
74

75
std::optional<AppError> AppUtils::check_for_errors(const Response& response)
76
{
24,933✔
77
    std::string error_msg;
24,933✔
78
    bool http_status_code_is_fatal = !AppUtils::is_success_status_code(response.http_status_code);
24,933✔
79

12,239✔
80
    try {
24,933✔
81
        auto ct = find_header("content-type", response.headers);
24,933✔
82
        if (ct && ct->second == "application/json" && !response.body.empty()) {
24,933✔
83
            auto body = nlohmann::json::parse(response.body);
6,255✔
84
            auto message = body.find("error");
6,255✔
85
            auto link = body.find("link");
6,255✔
86
            std::string parsed_link = link == body.end() ? "" : link->get<std::string>();
6,045✔
87

2,901✔
88
            if (auto error_code = body.find("error_code");
6,255✔
89
                error_code != body.end() && !error_code->get<std::string>().empty()) {
6,255✔
90
                auto server_error = error_code->get<std::string>();
440✔
91
                auto code = ErrorCodes::from_string(server_error);
440✔
92
                auto error_stg = message != body.end() ? message->get<std::string>() : "no error message";
439✔
93
                // If the err_code is not found or not an app error, create a generic AppError with
220✔
94
                // ErrorCodes::AppServerError "error_code" value from server response will be in the `server_error`
220✔
95
                // property
220✔
96
                if (code == ErrorCodes::UnknownError ||
440✔
97
                    !ErrorCodes::error_categories(code).test(ErrorCategory::app_error)) {
439✔
98
                    code = ErrorCodes::AppServerError;
188✔
99
                }
188✔
100
                return AppError(code, std::move(error_stg), std::move(parsed_link), response.http_status_code,
440✔
101
                                std::move(server_error));
440✔
102
            }
440✔
103
            // If the response only contains an error string, create a generic AppError with
2,681✔
104
            // ErrorCodes::AppUnknownError
2,681✔
105
            else if (message != body.end()) {
5,815✔
106
                return AppError(ErrorCodes::AppUnknownError, message->get<std::string>(), std::move(parsed_link),
52✔
107
                                response.http_status_code);
52✔
108
            }
52✔
109
        }
2✔
110
    }
24,933✔
111
    catch (const std::exception&) {
2✔
112
        // ignore parse errors from our attempt to read the error from json
1✔
113
    }
2✔
114

12,239✔
115
    if (response.client_error_code) {
24,687✔
116
        error_msg = response.body.empty() ? "client error code value considered fatal" : response.body;
11✔
117
        return AppError(*(response.client_error_code), error_msg, {}, response.http_status_code);
12✔
118
    }
12✔
119

11,987✔
120
    if (response.custom_status_code != 0) {
24,429✔
121
        error_msg = response.body.empty() ? "non-zero custom status code considered fatal" : response.body;
9✔
122
        return AppError(ErrorCodes::CustomError, error_msg, {}, response.custom_status_code);
10✔
123
    }
10✔
124

11,982✔
125
    if (http_status_code_is_fatal) {
24,419✔
126
        error_msg = response.body.empty() ? "http error code considered fatal"
41✔
127
                                          : "http error code considered fatal: " + response.body;
67✔
128
        return AppError(ErrorCodes::HTTPError, error_msg, {}, response.http_status_code);
72✔
129
    }
72✔
130

11,946✔
131
    return std::nullopt;
24,347✔
132
}
24,347✔
133

134
// Convert an AppError object into a Response object
135
Response AppUtils::make_apperror_response(const AppError& error)
136
{
376✔
137
    if (!error.server_error.empty() || error.code() == ErrorCodes::AppUnknownError) {
376✔
138
        auto body = nlohmann::json();
328✔
139
        body["error"] = error.reason();
328✔
140
        if (!error.server_error.empty()) {
328✔
141
            body["error_code"] = error.server_error;
324✔
142
        }
324✔
143
        if (!error.link_to_server_logs.empty()) {
328✔
144
            body["link"] = error.link_to_server_logs;
326✔
145
        }
326✔
146
        return {error.additional_status_code.value_or(0), 0, {{"content-type", "application/json"}}, body.dump()};
328✔
147
    }
328✔
148

24✔
149
    if (ErrorCodes::error_categories(error.code()).test(ErrorCategory::http_error)) {
48✔
150
        std::string message;
30✔
151
        // Try to extract the original body from the reason code
15✔
152
        static const char* match = "http error code considered fatal: ";
30✔
153
        if (auto pos = error.reason().find(match); pos != std::string::npos) {
30✔
154
            message = error.reason().substr(pos + std::char_traits<char>::length(match));
28✔
155
            // Remove the text added by AppError
14✔
156
            pos = message.find_last_of(".");
28✔
157
            if (pos != std::string::npos) {
28✔
158
                message.erase(pos);
28✔
159
            }
28✔
160
        }
28✔
161
        // Otherwise, body was originally empty
15✔
162
        return {error.additional_status_code.value_or(0), 0, {}, message};
30✔
163
    }
30✔
164
    if (ErrorCodes::error_categories(error.code()).test(ErrorCategory::custom_error)) {
18✔
165
        return {0, error.additional_status_code.value_or(0), {}, std::string(error.reason())};
6✔
166
    }
6✔
167

6✔
168
    // For other cases, put the error code in client_error_code field (client error or otherwise)
6✔
169
    return {error.additional_status_code.value_or(0), 0, {}, std::string(error.reason()), error.code()};
12✔
170
}
12✔
171

172
// Create a Response object with the given client error, message and optional http status code
173
Response AppUtils::make_clienterror_response(ErrorCodes::Error code, const std::string_view message,
174
                                             std::optional<int> http_status)
UNCOV
175
{
×
UNCOV
176
    return Response{http_status ? *http_status : 0, 0, {}, std::string(message), code};
×
UNCOV
177
}
×
178

179
} // namespace realm::app
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc