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

realm / realm-core / 2594

30 Aug 2024 02:48PM UTC coverage: 91.104% (-0.005%) from 91.109%
2594

push

Evergreen

web-flow
RCORE-2222 Remove 308 redirect tests (#7994)

* Removed redirect tests
* Removed one more location redirect test case

102824 of 181502 branches covered (56.65%)

157 of 161 new or added lines in 1 file covered. (97.52%)

61 existing lines in 21 files now uncovered.

217211 of 238421 relevant lines covered (91.1%)

5771330.6 hits per line

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

96.15
/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
{
37,671✔
34
    for (auto&& current : search_map) {
43,462✔
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) {
43,462✔
41
            return &current;
18,147✔
42
        }
18,147✔
43
#endif
43,462✔
44
    }
43,462✔
45
    return nullptr;
19,524✔
46
}
37,671✔
47

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

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

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

75
// Create a Response object with the given client error, message and optional http status code
76
Response AppUtils::make_clienterror_response(ErrorCodes::Error code, const std::string_view message,
77
                                             std::optional<int> http_status)
78
{
×
79
    return Response{http_status ? *http_status : 0, 0, {}, std::string(message), code};
×
80
}
×
81

82
#if REALM_APP_SERVICES
83
std::optional<AppError> AppUtils::check_for_errors(const Response& response)
84
{
25,859✔
85
    std::string error_msg;
25,859✔
86
    bool http_status_code_is_fatal = !AppUtils::is_success_status_code(response.http_status_code);
25,859✔
87

88
    try {
25,859✔
89
        auto ct = find_header("content-type", response.headers);
25,859✔
90
        if (ct && ct->second == "application/json" && !response.body.empty()) {
25,859✔
91
            auto body = nlohmann::json::parse(response.body);
6,335✔
92
            auto message = body.find("error");
6,335✔
93
            auto link = body.find("link");
6,335✔
94
            std::string parsed_link = link == body.end() ? "" : link->get<std::string>();
6,335✔
95

96
            if (auto error_code = body.find("error_code");
6,335✔
97
                error_code != body.end() && !error_code->get<std::string>().empty()) {
6,335✔
98
                auto server_error = error_code->get<std::string>();
440✔
99
                auto code = ErrorCodes::from_string(server_error);
440✔
100
                auto error_stg = message != body.end() ? message->get<std::string>() : "no error message";
440✔
101
                // If the err_code is not found or not an app error, create a generic AppError with
102
                // ErrorCodes::AppServerError "error_code" value from server response will be in the `server_error`
103
                // property
104
                if (code == ErrorCodes::UnknownError ||
440✔
105
                    !ErrorCodes::error_categories(code).test(ErrorCategory::app_error)) {
440✔
106
                    code = ErrorCodes::AppServerError;
188✔
107
                }
188✔
108
                return AppError(code, std::move(error_stg), std::move(parsed_link), response.http_status_code,
440✔
109
                                std::move(server_error));
440✔
110
            }
440✔
111
            // If the response only contains an error string, create a generic AppError with
112
            // ErrorCodes::AppUnknownError
113
            else if (message != body.end()) {
5,895✔
114
                return AppError(ErrorCodes::AppUnknownError, message->get<std::string>(), std::move(parsed_link),
52✔
115
                                response.http_status_code);
52✔
116
            }
52✔
117
        }
6,335✔
118
    }
25,859✔
119
    catch (const std::exception&) {
25,859✔
120
        // ignore parse errors from our attempt to read the error from json
121
    }
2✔
122

123
    if (response.client_error_code) {
25,367✔
124
        error_msg = response.body.empty() ? "client error code value considered fatal" : response.body;
4✔
125
        return AppError(*(response.client_error_code), error_msg, {}, response.http_status_code);
4✔
126
    }
4✔
127

128
    if (response.custom_status_code != 0) {
25,363✔
129
        error_msg = response.body.empty() ? "non-zero custom status code considered fatal" : response.body;
32✔
130
        return AppError(ErrorCodes::CustomError, error_msg, {}, response.custom_status_code);
32✔
131
    }
32✔
132

133
    if (http_status_code_is_fatal) {
25,331✔
134
        error_msg = response.body.empty() ? "http error code considered fatal"
80✔
135
                                          : "http error code considered fatal: " + response.body;
80✔
136
        return AppError(ErrorCodes::HTTPError, error_msg, {}, response.http_status_code);
80✔
137
    }
80✔
138

139
    return std::nullopt;
25,251✔
140
}
25,331✔
141

142
// Convert an AppError object into a Response object
143
Response AppUtils::make_apperror_response(const AppError& error)
144
{
376✔
145
    if (!error.server_error.empty() || error.code() == ErrorCodes::AppUnknownError) {
376✔
146
        auto body = nlohmann::json();
328✔
147
        body["error"] = error.reason();
328✔
148
        if (!error.server_error.empty()) {
328✔
149
            body["error_code"] = error.server_error;
324✔
150
        }
324✔
151
        if (!error.link_to_server_logs.empty()) {
328✔
152
            body["link"] = error.link_to_server_logs;
326✔
153
        }
326✔
154
        return {error.additional_status_code.value_or(0), 0, {{"content-type", "application/json"}}, body.dump()};
328✔
155
    }
328✔
156

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

176
    // For other cases, put the error code in client_error_code field (client error or otherwise)
177
    return {error.additional_status_code.value_or(0), 0, {}, std::string(error.reason()), error.code()};
4✔
178
}
18✔
179

180
#endif // REALM_APP_SERVICES
181

182
} // 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