Line data Source code
1 : #ifndef THINGER_HTTP_CLIENT_RESPONSE_HPP
2 : #define THINGER_HTTP_CLIENT_RESPONSE_HPP
3 :
4 : #include "../common/http_response.hpp"
5 : #include <boost/system/error_code.hpp>
6 : #include <cassert>
7 : #include <memory>
8 : #include <nlohmann/json.hpp>
9 :
10 : namespace thinger::http {
11 :
12 : class client_response {
13 : private:
14 : std::shared_ptr<http_response> response_;
15 : boost::system::error_code error_;
16 :
17 : public:
18 : // Constructors
19 0 : client_response() = default;
20 :
21 7891 : client_response(const boost::system::error_code& ec,
22 : std::shared_ptr<http_response> res)
23 7891 : : response_(std::move(res)), error_(ec) {}
24 :
25 : // Status checks
26 6224 : bool ok() const {
27 11689 : return !error_ && response_ && static_cast<int>(response_->get_status()) >= 200 &&
28 11689 : static_cast<int>(response_->get_status()) < 300;
29 : }
30 :
31 : bool success() const { return ok(); }
32 :
33 210 : operator bool() const { return !error_ && response_; }
34 :
35 93 : bool has_error() const { return error_ || !response_; }
36 :
37 144 : bool has_network_error() const { return error_.value() != 0; }
38 :
39 63 : bool has_http_error() const {
40 63 : return !error_ && response_ && static_cast<int>(response_->get_status()) >= 400;
41 : }
42 :
43 : // Content access
44 776 : const std::string& body() const {
45 776 : static const std::string empty;
46 776 : return response_ ? response_->get_content() : empty;
47 : }
48 :
49 : std::string_view text() const { return body(); }
50 :
51 397 : nlohmann::json json() const {
52 397 : if (!response_ || response_->get_content().empty()) {
53 0 : return nlohmann::json();
54 : }
55 397 : auto j = nlohmann::json::parse(response_->get_content(), nullptr, false);
56 397 : if (j.is_discarded()) {
57 0 : return nlohmann::json();
58 : }
59 397 : return j;
60 397 : }
61 :
62 : // Status info
63 887 : int status() const {
64 887 : return response_ ? static_cast<int>(response_->get_status()) : 0;
65 : }
66 :
67 30 : int status_code() const { return status(); }
68 :
69 21 : bool is_redirect() const {
70 21 : return response_ && response_->is_redirect_response();
71 : }
72 :
73 7 : bool is_client_error() const {
74 7 : return response_ && status() >= 400 && status() < 500;
75 : }
76 :
77 7 : bool is_server_error() const {
78 7 : return response_ && status() >= 500;
79 : }
80 :
81 : // Headers
82 139 : std::string header(const std::string& key) const {
83 139 : return response_ && response_->has_header(key) ?
84 417 : response_->get_header(key) : "";
85 : }
86 :
87 42 : bool has_header(const std::string& key) const {
88 42 : return response_ && response_->has_header(key);
89 : }
90 :
91 : // Error info
92 246 : std::string error() const {
93 246 : if (error_) {
94 183 : return error_.message();
95 63 : } else if (!response_) {
96 0 : return "No response received";
97 63 : } else if (has_http_error()) {
98 30 : return "HTTP error " + std::to_string(status());
99 : }
100 66 : return "";
101 : }
102 :
103 51 : std::string error_message() const { return error(); }
104 :
105 1133 : const boost::system::error_code& error_code() const { return error_; }
106 :
107 : // Advanced access
108 : const http_response* operator->() const { return response_.get(); }
109 :
110 : const http_response& operator*() const {
111 : assert(response_ && "Dereferencing empty client_response");
112 : return *response_;
113 : }
114 :
115 24 : std::shared_ptr<http_response> get() const { return response_; }
116 :
117 : // Content type helpers
118 16 : std::string content_type() const {
119 32 : return header("Content-Type");
120 : }
121 :
122 : size_t content_length() const {
123 : if (!response_) return 0;
124 : return response_->get_content_size();
125 : }
126 :
127 8 : bool is_json() const {
128 8 : auto ct = content_type();
129 8 : return ct.find("application/json") != std::string::npos
130 16 : || ct.find("+json") != std::string::npos;
131 8 : }
132 :
133 : bool is_html() const {
134 : auto ct = content_type();
135 : return ct.find("text/html") != std::string::npos;
136 : }
137 :
138 : bool is_text() const {
139 : auto ct = content_type();
140 : return ct.find("text/") == 0;
141 : }
142 : };
143 :
144 : } // namespace thinger::http
145 :
146 : #endif // THINGER_HTTP_CLIENT_RESPONSE_HPP
|