feat(tcp): socket errors as std::system_error, with the phase that failed
SocketErrorException is now a std::system_error: code() carries the errno
under std::generic_category(), or getaddrinfo's result under the new
ResolverCategory(), whose message() is gai_strerror's text; `phase` says
which step failed (resolve, open, connect, send, receive). It is still a
std::runtime_error with the message ClientTCP always threw, the code's
text after it, so existing handlers keep working.
Before, the exception carried a `kind` and an int, and a caller that had
to print the cause its own way still had to match the message prefix to
learn where the failure happened. The standard type ends both: the
category knows how to print the number, and the phase is a field. A
caller writes "[Errno {}] {}" from code().value() and code().message(),
or compares code() with std::errc::connection_refused.
Breaking: SocketErrorException's `kind` and `code` members are gone;
read code().category() and code().value().
This commit is contained in:
parent
16da0a1979
commit
e86c3b70a0
9 changed files with 107 additions and 63 deletions
|
|
@ -37,26 +37,42 @@ namespace Crafter {
|
|||
}
|
||||
};
|
||||
|
||||
// Where the code of a SocketErrorException comes from.
|
||||
export enum class SocketErrorKind {
|
||||
// An errno: ECONNREFUSED, ENETUNREACH, EMFILE, ...
|
||||
System,
|
||||
// A getaddrinfo result: EAI_NONAME, EAI_AGAIN, ...
|
||||
Resolver,
|
||||
// The step of a connection a SocketErrorException failed at.
|
||||
export enum class SocketPhase {
|
||||
// getaddrinfo on the host name.
|
||||
Resolve,
|
||||
// socket() for an address the host resolved to.
|
||||
Open,
|
||||
// connect() to that address.
|
||||
Connect,
|
||||
Send,
|
||||
Receive,
|
||||
};
|
||||
|
||||
// A resolve, a socket(), a connect, a send or a receive that failed, with
|
||||
// the code it failed with. what() is the text ClientTCP has always thrown, and catching
|
||||
// std::runtime_error still catches it; the code is for callers that
|
||||
// report the cause in their own words — a Python-style
|
||||
// "[Errno 111] Connection refused", say — and would otherwise have to
|
||||
// parse it back out of the message.
|
||||
export class SocketErrorException : public std::runtime_error {
|
||||
// The error category of getaddrinfo's results (EAI_NONAME, EAI_AGAIN,
|
||||
// ...): a number space of its own that overlaps the errno values, so a
|
||||
// resolver code is never confused with an errno, and whose message() is
|
||||
// gai_strerror's text. One instance for the process, like
|
||||
// std::generic_category().
|
||||
export const std::error_category& ResolverCategory() noexcept;
|
||||
|
||||
// A resolve, a socket(), a connect, a send or a receive that failed. A
|
||||
// std::system_error, so still a std::runtime_error for the handlers that
|
||||
// catch one: code() is the errno under std::generic_category(), or the
|
||||
// getaddrinfo result under ResolverCategory(), and what() is the text
|
||||
// ClientTCP has always thrown, the code's own text after it. `phase`
|
||||
// says which step failed. A caller that reports the cause in its own
|
||||
// words — "[Errno 111] Connection refused", say — takes code().value()
|
||||
// and code().message() instead of parsing what(), and compares code()
|
||||
// with std::errc::connection_refused instead of the number.
|
||||
export class SocketErrorException : public std::system_error {
|
||||
public:
|
||||
SocketErrorException(SocketErrorKind kind, std::int32_t code, const std::string& message)
|
||||
: std::runtime_error(message), kind(kind), code(code) {}
|
||||
SocketErrorKind kind;
|
||||
std::int32_t code;
|
||||
SocketErrorException(SocketPhase phase, std::error_code code, const std::string& what)
|
||||
: std::system_error(code, what), phase(phase) {}
|
||||
// Without a message of its own: what() is the code's text alone.
|
||||
SocketErrorException(SocketPhase phase, std::error_code code)
|
||||
: std::system_error(code), phase(phase) {}
|
||||
SocketPhase phase;
|
||||
};
|
||||
|
||||
export class ClientTCP {
|
||||
|
|
|
|||
Loading…
Reference in a new issue