Skip to content

Commit

Permalink
Merge pull request #1110 from libcpr/feature/better-curl-error-code-h…
Browse files Browse the repository at this point in the history
…andling

More And Better cpr Error Codes
  • Loading branch information
COM8 authored Sep 22, 2024
2 parents 1d45df8 + c698b9a commit 99f044e
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 64 deletions.
129 changes: 100 additions & 29 deletions cpr/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,59 +10,130 @@ ErrorCode Error::getErrorCodeForCurlError(std::int32_t curl_code) {
return ErrorCode::OK;
case CURLE_UNSUPPORTED_PROTOCOL:
return ErrorCode::UNSUPPORTED_PROTOCOL;
case CURLE_FAILED_INIT:
return ErrorCode::FAILED_INIT;
case CURLE_URL_MALFORMAT:
return ErrorCode::INVALID_URL_FORMAT;
return ErrorCode::URL_MALFORMAT;
case CURLE_NOT_BUILT_IN:
return ErrorCode::NOT_BUILT_IN;
case CURLE_COULDNT_RESOLVE_PROXY:
return ErrorCode::PROXY_RESOLUTION_FAILURE;
return ErrorCode::COULDNT_RESOLVE_PROXY;
case CURLE_COULDNT_RESOLVE_HOST:
return ErrorCode::HOST_RESOLUTION_FAILURE;
return ErrorCode::COULDNT_RESOLVE_HOST;
case CURLE_COULDNT_CONNECT:
return ErrorCode::CONNECTION_FAILURE;
return ErrorCode::COULDNT_CONNECT;
case CURLE_WEIRD_SERVER_REPLY:
return ErrorCode::WEIRD_SERVER_REPLY;
case CURLE_REMOTE_ACCESS_DENIED:
return ErrorCode::REMOTE_ACCESS_DENIED;
case CURLE_HTTP2:
return ErrorCode::HTTP2;
case CURLE_QUOTE_ERROR:
return ErrorCode::QUOTE_ERROR;
case CURLE_HTTP_RETURNED_ERROR:
return ErrorCode::HTTP_RETURNED_ERROR;
case CURLE_WRITE_ERROR:
return ErrorCode::WRITE_ERROR;
case CURLE_UPLOAD_FAILED:
return ErrorCode::UPLOAD_FAILED;
case CURLE_READ_ERROR:
return ErrorCode::READ_ERROR;
case CURLE_OUT_OF_MEMORY:
return ErrorCode::OUT_OF_MEMORY;
case CURLE_OPERATION_TIMEDOUT:
return ErrorCode::OPERATION_TIMEDOUT;
case CURLE_RANGE_ERROR:
return ErrorCode::RANGE_ERROR;
case CURLE_HTTP_POST_ERROR:
return ErrorCode::HTTP_POST_ERROR;
case CURLE_SSL_CONNECT_ERROR:
return ErrorCode::SSL_CONNECT_ERROR;
#if LIBCURL_VERSION_NUM < 0x073e00
case CURLE_PEER_FAILED_VERIFICATION:
return ErrorCode::SSL_REMOTE_CERTIFICATE_ERROR;
#endif
case CURLE_BAD_DOWNLOAD_RESUME:
return ErrorCode::BAD_DOWNLOAD_RESUME;
case CURLE_FILE_COULDNT_READ_FILE:
return ErrorCode::FILE_COULDNT_READ_FILE;
case CURLE_FUNCTION_NOT_FOUND:
return ErrorCode::FUNCTION_NOT_FOUND;
case CURLE_ABORTED_BY_CALLBACK:
case CURLE_WRITE_ERROR:
return ErrorCode::REQUEST_CANCELLED;
return ErrorCode::ABORTED_BY_CALLBACK;
case CURLE_BAD_FUNCTION_ARGUMENT:
return ErrorCode::BAD_FUNCTION_ARGUMENT;
case CURLE_INTERFACE_FAILED:
return ErrorCode::INTERFACE_FAILED;
case CURLE_OBSOLETE46:
return ErrorCode::OBSOLETE46;
case CURLE_TOO_MANY_REDIRECTS:
return ErrorCode::TOO_MANY_REDIRECTS;
case CURLE_UNKNOWN_OPTION:
return ErrorCode::UNKNOWN_OPTION;
case CURLE_SETOPT_OPTION_SYNTAX:
return ErrorCode::SETOPT_OPTION_SYNTAX;
case CURLE_GOT_NOTHING:
return ErrorCode::EMPTY_RESPONSE;
return ErrorCode::GOT_NOTHING;
case CURLE_SSL_ENGINE_NOTFOUND:
return ErrorCode::SSL_ENGINE_NOTFOUND;
case CURLE_SSL_ENGINE_SETFAILED:
return ErrorCode::GENERIC_SSL_ERROR;
return ErrorCode::SSL_ENGINE_SETFAILED;
case CURLE_SEND_ERROR:
return ErrorCode::NETWORK_SEND_FAILURE;
return ErrorCode::SEND_ERROR;
case CURLE_RECV_ERROR:
return ErrorCode::NETWORK_RECEIVE_ERROR;
return ErrorCode::RECV_ERROR;
case CURLE_SSL_CERTPROBLEM:
return ErrorCode::SSL_LOCAL_CERTIFICATE_ERROR;
return ErrorCode::SSL_CERTPROBLEM;
case CURLE_SSL_CIPHER:
return ErrorCode::GENERIC_SSL_ERROR;
#if LIBCURL_VERSION_NUM >= 0x073e00
return ErrorCode::SSL_CIPHER;
case CURLE_PEER_FAILED_VERIFICATION:
return ErrorCode::SSL_REMOTE_CERTIFICATE_ERROR;
#else
case CURLE_SSL_CACERT:
return ErrorCode::SSL_CACERT_ERROR;
#endif
return ErrorCode::PEER_FAILED_VERIFICATION;
case CURLE_BAD_CONTENT_ENCODING:
return ErrorCode::BAD_CONTENT_ENCODING;
case CURLE_FILESIZE_EXCEEDED:
return ErrorCode::FILESIZE_EXCEEDED;
case CURLE_USE_SSL_FAILED:
return ErrorCode::USE_SSL_FAILED;
case CURLE_SEND_FAIL_REWIND:
return ErrorCode::SEND_FAIL_REWIND;
case CURLE_SSL_ENGINE_INITFAILED:
return ErrorCode::GENERIC_SSL_ERROR;
return ErrorCode::SSL_ENGINE_INITFAILED;
case CURLE_LOGIN_DENIED:
return ErrorCode::LOGIN_DENIED;
case CURLE_SSL_CACERT_BADFILE:
return ErrorCode::SSL_CACERT_ERROR;
return ErrorCode::SSL_CACERT_BADFILE;
case CURLE_SSL_SHUTDOWN_FAILED:
return ErrorCode::GENERIC_SSL_ERROR;
return ErrorCode::SSL_SHUTDOWN_FAILED;
case CURLE_AGAIN:
return ErrorCode::AGAIN;
case CURLE_SSL_CRL_BADFILE:
return ErrorCode::SSL_CRL_BADFILE;
case CURLE_SSL_ISSUER_ERROR:
return ErrorCode::SSL_CACERT_ERROR;
case CURLE_TOO_MANY_REDIRECTS:
return ErrorCode::TOO_MANY_REDIRECTS;
return ErrorCode::SSL_ISSUER_ERROR;
case CURLE_CHUNK_FAILED:
return ErrorCode::CHUNK_FAILED;
case CURLE_NO_CONNECTION_AVAILABLE:
return ErrorCode::NO_CONNECTION_AVAILABLE;
case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
return ErrorCode::SSL_PINNEDPUBKEYNOTMATCH;
case CURLE_SSL_INVALIDCERTSTATUS:
return ErrorCode::SSL_INVALIDCERTSTATUS;
case CURLE_HTTP2_STREAM:
return ErrorCode::HTTP2_STREAM;
case CURLE_RECURSIVE_API_CALL:
return ErrorCode::RECURSIVE_API_CALL;
case CURLE_AUTH_ERROR:
return ErrorCode::AUTH_ERROR;
case CURLE_HTTP3:
return ErrorCode::HTTP3;
case CURLE_QUIC_CONNECT_ERROR:
return ErrorCode::QUIC_CONNECT_ERROR;
case CURLE_PROXY:
return ErrorCode::PROXY;
case CURLE_SSL_CLIENTCERT:
return ErrorCode::SSL_CLIENTCERT;
case CURLE_UNRECOVERABLE_POLL:
return ErrorCode::UNRECOVERABLE_POLL;
case CURLE_TOO_LARGE:
return ErrorCode::TOO_LARGE;
default:
return ErrorCode::INTERNAL_ERROR;
return ErrorCode::UNKNOWN_ERROR;
}
}

Expand Down
89 changes: 74 additions & 15 deletions include/cpr/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,91 @@

namespace cpr {

/**
* cpr error codes that match the ones found inside 'curl.h'.
* These error codes only include relevant error codes meaning no support for e.g. FTP errors since cpr does only support HTTP.
**/
enum class ErrorCode {
/**
* Everything is good and no error occurred.
**/
OK = 0,
CONNECTION_FAILURE,
EMPTY_RESPONSE,
HOST_RESOLUTION_FAILURE,
INTERNAL_ERROR,
INVALID_URL_FORMAT,
NETWORK_RECEIVE_ERROR,
NETWORK_SEND_FAILURE,
UNSUPPORTED_PROTOCOL,
FAILED_INIT,
URL_MALFORMAT,
NOT_BUILT_IN,
COULDNT_RESOLVE_PROXY,
COULDNT_RESOLVE_HOST,
COULDNT_CONNECT,
WEIRD_SERVER_REPLY,
REMOTE_ACCESS_DENIED,
HTTP2,
PARTIAL_FILE,
QUOTE_ERROR,
HTTP_RETURNED_ERROR,
WRITE_ERROR,
UPLOAD_FAILED,
READ_ERROR,
OUT_OF_MEMORY,
OPERATION_TIMEDOUT,
PROXY_RESOLUTION_FAILURE,
RANGE_ERROR,
HTTP_POST_ERROR,
SSL_CONNECT_ERROR,
SSL_LOCAL_CERTIFICATE_ERROR,
SSL_REMOTE_CERTIFICATE_ERROR,
SSL_CACERT_ERROR,
GENERIC_SSL_ERROR,
UNSUPPORTED_PROTOCOL,
REQUEST_CANCELLED,
BAD_DOWNLOAD_RESUME,
FILE_COULDNT_READ_FILE,
FUNCTION_NOT_FOUND,
ABORTED_BY_CALLBACK,
BAD_FUNCTION_ARGUMENT,
INTERFACE_FAILED,
OBSOLETE46,
TOO_MANY_REDIRECTS,
UNKNOWN_OPTION,
SETOPT_OPTION_SYNTAX,
GOT_NOTHING,
SSL_ENGINE_NOTFOUND,
SSL_ENGINE_SETFAILED,
SEND_ERROR,
RECV_ERROR,
SSL_CERTPROBLEM,
SSL_CIPHER,
PEER_FAILED_VERIFICATION,
BAD_CONTENT_ENCODING,
FILESIZE_EXCEEDED,
USE_SSL_FAILED,
SEND_FAIL_REWIND,
SSL_ENGINE_INITFAILED,
LOGIN_DENIED,
SSL_CACERT_BADFILE,
SSL_SHUTDOWN_FAILED,
AGAIN,
SSL_CRL_BADFILE,
SSL_ISSUER_ERROR,
CHUNK_FAILED,
NO_CONNECTION_AVAILABLE,
SSL_PINNEDPUBKEYNOTMATCH,
SSL_INVALIDCERTSTATUS,
HTTP2_STREAM,
RECURSIVE_API_CALL,
AUTH_ERROR,
HTTP3,
QUIC_CONNECT_ERROR,
PROXY,
SSL_CLIENTCERT,
UNRECOVERABLE_POLL,
TOO_LARGE,
/**
* An unknown error inside curl occurred.
* Please try to reproduce it and then report it to us.
* It might be that there is a new curl error code we are not aware yet.
* Reporting bugs: https://github.com/libcpr/cpr
**/
UNKNOWN_ERROR = 1000,
};

class Error {
public:
ErrorCode code = ErrorCode::OK;
std::string message{};
std::string message;

Error() = default;

Expand Down
8 changes: 4 additions & 4 deletions test/callback_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ TEST(CallbackPatchTests, CallbackPatchFunctionTextReferenceTest) {
TEST(CallbackDataTests, CallbackReadFunctionCancelTest) {
Url url{server->GetBaseUrl() + "/url_post.html"};
Response response = cpr::Post(url, cpr::ReadCallback([](char* /*buffer*/, size_t& /*size*/, intptr_t /*userdata*/) -> size_t { return false; }));
EXPECT_EQ(response.error.code, ErrorCode::REQUEST_CANCELLED);
EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR));
}

TEST(CallbackDataTests, CallbackReadFunctionTextTest) {
Expand Down Expand Up @@ -858,7 +858,7 @@ TEST(CallbackDataTests, CallbackReadFunctionChunkedTest) {
TEST(CallbackDataTests, CallbackHeaderFunctionCancelTest) {
Url url{server->GetBaseUrl() + "/url_post.html"};
Response response = Post(url, HeaderCallback{[](const std::string_view& /*header*/, intptr_t /*userdata*/) -> bool { return false; }});
EXPECT_EQ(response.error.code, ErrorCode::REQUEST_CANCELLED);
EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR));
}

TEST(CallbackDataTests, CallbackHeaderFunctionTextTest) {
Expand All @@ -878,7 +878,7 @@ TEST(CallbackDataTests, CallbackHeaderFunctionTextTest) {
TEST(CallbackDataTests, CallbackWriteFunctionCancelTest) {
Url url{server->GetBaseUrl() + "/url_post.html"};
Response response = Post(url, WriteCallback{[](const std::string_view& /*header*/, intptr_t /*userdata*/) -> bool { return false; }});
EXPECT_EQ(response.error.code, ErrorCode::REQUEST_CANCELLED);
EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR));
}

TEST(CallbackDataTests, CallbackWriteFunctionTextTest) {
Expand All @@ -898,7 +898,7 @@ TEST(CallbackDataTests, CallbackWriteFunctionTextTest) {
TEST(CallbackDataTests, CallbackProgressFunctionCancelTest) {
Url url{server->GetBaseUrl() + "/url_post.html"};
Response response = Post(url, ProgressCallback{[](size_t /*downloadTotal*/, size_t /*downloadNow*/, size_t /*uploadTotal*/, size_t /*uploadNow*/, intptr_t /*userdata*/) -> bool { return false; }});
EXPECT_EQ(response.error.code, ErrorCode::REQUEST_CANCELLED);
EXPECT_TRUE((response.error.code == ErrorCode::ABORTED_BY_CALLBACK) || (response.error.code == ErrorCode::WRITE_ERROR));
}

TEST(CallbackDataTests, CallbackProgressFunctionTotalTest) {
Expand Down
12 changes: 6 additions & 6 deletions test/error_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ TEST(ErrorTests, InvalidURLFailure) {
Url url{"???"};
Response response = cpr::Get(url);
EXPECT_EQ(0, response.status_code);
EXPECT_EQ(ErrorCode::INVALID_URL_FORMAT, response.error.code);
EXPECT_EQ(ErrorCode::URL_MALFORMAT, response.error.code);
}

TEST(ErrorTests, TimeoutFailure) {
Expand All @@ -45,16 +45,16 @@ TEST(ErrorTests, ConnectTimeoutFailure) {
Url url{"http://localhost:67"};
Response response = cpr::Get(url, cpr::ConnectTimeout{1});
EXPECT_EQ(0, response.status_code);
// Sometimes a CONNECTION_FAILURE happens before the OPERATION_TIMEDOUT:
EXPECT_TRUE(response.error.code == ErrorCode::OPERATION_TIMEDOUT || response.error.code == ErrorCode::CONNECTION_FAILURE);
// Sometimes a COULDNT_CONNECT happens before the OPERATION_TIMEDOUT:
EXPECT_TRUE(response.error.code == ErrorCode::OPERATION_TIMEDOUT || response.error.code == ErrorCode::COULDNT_CONNECT);
}

TEST(ErrorTests, ChronoConnectTimeoutFailure) {
Url url{"http://localhost:67"};
Response response = cpr::Get(url, cpr::ConnectTimeout{std::chrono::milliseconds{1}});
EXPECT_EQ(0, response.status_code);
// Sometimes a CONNECTION_FAILURE happens before the OPERATION_TIMEDOUT:
EXPECT_TRUE(response.error.code == ErrorCode::OPERATION_TIMEDOUT || response.error.code == ErrorCode::CONNECTION_FAILURE);
// Sometimes a COULDNT_CONNECT happens before the OPERATION_TIMEDOUT:
EXPECT_TRUE(response.error.code == ErrorCode::OPERATION_TIMEDOUT || response.error.code == ErrorCode::COULDNT_CONNECT);
}

TEST(ErrorTests, LowSpeedTimeFailure) {
Expand All @@ -76,7 +76,7 @@ TEST(ErrorTests, ProxyFailure) {
Response response = cpr::Get(url, cpr::Proxies{{"http", "http://bad_host.libcpr.org"}});
EXPECT_EQ(url, response.url);
EXPECT_EQ(0, response.status_code);
EXPECT_EQ(ErrorCode::PROXY_RESOLUTION_FAILURE, response.error.code);
EXPECT_EQ(ErrorCode::COULDNT_RESOLVE_PROXY, response.error.code);
}

TEST(ErrorTests, BoolFalseTest) {
Expand Down
2 changes: 1 addition & 1 deletion test/get_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ TEST(BasicTests, BadHostTest) {
EXPECT_EQ(std::string{}, response.text);
EXPECT_EQ(url, response.url);
EXPECT_EQ(0, response.status_code);
EXPECT_EQ(ErrorCode::HOST_RESOLUTION_FAILURE, response.error.code);
EXPECT_EQ(ErrorCode::COULDNT_RESOLVE_HOST, response.error.code);
}

TEST(CookiesTests, BasicCookiesTest) {
Expand Down
2 changes: 1 addition & 1 deletion test/head_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ TEST(HeadTests, BadHostHeadTest) {
EXPECT_EQ(std::string{}, response.text);
EXPECT_EQ(url, response.url);
EXPECT_EQ(0, response.status_code);
EXPECT_EQ(ErrorCode::HOST_RESOLUTION_FAILURE, response.error.code);
EXPECT_EQ(ErrorCode::COULDNT_RESOLVE_HOST, response.error.code);
}

TEST(HeadTests, CookieHeadTest) {
Expand Down
2 changes: 1 addition & 1 deletion test/post_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ TEST(UrlEncodedPostTests, UrlPostBadHostTest) {
EXPECT_EQ(url, response.url);
EXPECT_EQ(std::string{}, response.header["content-type"]);
EXPECT_EQ(0, response.status_code);
EXPECT_EQ(ErrorCode::HOST_RESOLUTION_FAILURE, response.error.code);
EXPECT_EQ(ErrorCode::COULDNT_RESOLVE_HOST, response.error.code);
}

TEST(UrlEncodedPostTests, FormPostSingleTest) {
Expand Down
2 changes: 1 addition & 1 deletion test/raw_body_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ TEST(BodyPostTests, UrlPostBadHostTest) {
EXPECT_EQ(url, response.url);
EXPECT_EQ(std::string{}, response.header["content-type"]);
EXPECT_EQ(0, response.status_code);
EXPECT_EQ(ErrorCode::HOST_RESOLUTION_FAILURE, response.error.code);
EXPECT_EQ(ErrorCode::COULDNT_RESOLVE_HOST, response.error.code);
}

TEST(BodyPostTests, StringMoveBodyTest) {
Expand Down
Loading

0 comments on commit 99f044e

Please sign in to comment.