Skip to content

Commit 13016cc

Browse files
committed
feat(utils): add network error classification helper
1 parent 873944d commit 13016cc

1 file changed

Lines changed: 136 additions & 0 deletions

File tree

include/vix/utils/NetworkError.hpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
*
3+
* @file NetworkError.hpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2026, Gaspard Kirira. All rights reserved.
7+
* https://github.com/vixcpp/vix
8+
* Use of this source code is governed by a MIT license
9+
* that can be found in the License file.
10+
*
11+
* Vix.cpp
12+
*/
13+
#ifndef VIX_UTILS_NETWORK_ERROR_HPP
14+
#define VIX_UTILS_NETWORK_ERROR_HPP
15+
16+
#include <cctype>
17+
#include <cstddef>
18+
#include <cerrno>
19+
#include <string_view>
20+
#include <system_error>
21+
22+
namespace vix::utils
23+
{
24+
inline bool contains_token_icase(std::string_view text, std::string_view token)
25+
{
26+
if (token.empty())
27+
{
28+
return true;
29+
}
30+
31+
if (token.size() > text.size())
32+
{
33+
return false;
34+
}
35+
36+
const auto lower_char = [](unsigned char c) -> char
37+
{
38+
return static_cast<char>(std::tolower(c));
39+
};
40+
41+
const char first = lower_char(static_cast<unsigned char>(token.front()));
42+
const std::size_t last = text.size() - token.size();
43+
44+
for (std::size_t i = 0; i <= last; ++i)
45+
{
46+
if (lower_char(static_cast<unsigned char>(text[i])) != first)
47+
{
48+
continue;
49+
}
50+
51+
std::size_t j = 1;
52+
53+
for (; j < token.size(); ++j)
54+
{
55+
if (lower_char(static_cast<unsigned char>(text[i + j])) !=
56+
lower_char(static_cast<unsigned char>(token[j])))
57+
{
58+
break;
59+
}
60+
}
61+
62+
if (j == token.size())
63+
{
64+
return true;
65+
}
66+
}
67+
68+
return false;
69+
}
70+
71+
inline bool is_normal_network_disconnect_message(std::string_view message) noexcept
72+
{
73+
return contains_token_icase(message, "broken pipe") ||
74+
contains_token_icase(message, "connection reset") ||
75+
contains_token_icase(message, "connection reset by peer") ||
76+
contains_token_icase(message, "operation canceled") ||
77+
contains_token_icase(message, "operation cancelled") ||
78+
contains_token_icase(message, "canceled") ||
79+
contains_token_icase(message, "cancelled") ||
80+
contains_token_icase(message, "end of file") ||
81+
contains_token_icase(message, "eof");
82+
}
83+
84+
inline bool is_normal_network_disconnect(const std::system_error &e) noexcept
85+
{
86+
const auto code = e.code();
87+
88+
if (code == std::errc::operation_canceled ||
89+
code == std::errc::broken_pipe ||
90+
code == std::errc::connection_reset ||
91+
code == std::errc::connection_aborted ||
92+
code == std::errc::timed_out)
93+
{
94+
return true;
95+
}
96+
97+
#ifdef ECANCELED
98+
if (code.value() == ECANCELED)
99+
{
100+
return true;
101+
}
102+
#endif
103+
104+
#ifdef EPIPE
105+
if (code.value() == EPIPE)
106+
{
107+
return true;
108+
}
109+
#endif
110+
111+
#ifdef ECONNRESET
112+
if (code.value() == ECONNRESET)
113+
{
114+
return true;
115+
}
116+
#endif
117+
118+
#ifdef ECONNABORTED
119+
if (code.value() == ECONNABORTED)
120+
{
121+
return true;
122+
}
123+
#endif
124+
125+
#ifdef ETIMEDOUT
126+
if (code.value() == ETIMEDOUT)
127+
{
128+
return true;
129+
}
130+
#endif
131+
132+
return is_normal_network_disconnect_message(e.what());
133+
}
134+
}
135+
136+
#endif

0 commit comments

Comments
 (0)