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 : client_response() = default;
20 :
21 874 : client_response(const boost::system::error_code& ec,
22 : std::shared_ptr<http_response> res)
23 874 : : response_(std::move(res)), error_(ec) {}
24 :
25 : // Status checks
26 769 : bool ok() const {
27 1504 : return !error_ && response_ && static_cast<int>(response_->get_status()) >= 200 &&
28 1504 : static_cast<int>(response_->get_status()) < 300;
29 : }
30 :
31 : bool success() const { return ok(); }
32 :
33 : operator bool() const { return !error_ && response_; }
34 :
35 : bool has_error() const { return error_ || !response_; }
36 :
37 : bool has_network_error() const { return error_.value() != 0; }
38 :
39 0 : bool has_http_error() const {
40 0 : return !error_ && response_ && static_cast<int>(response_->get_status()) >= 400;
41 : }
42 :
43 : // Content access
44 54 : const std::string& body() const {
45 54 : static const std::string empty;
46 54 : return response_ ? response_->get_content() : empty;
47 : }
48 :
49 : std::string_view text() const { return body(); }
50 :
51 248 : nlohmann::json json() const {
52 248 : if (!response_ || response_->get_content().empty()) {
53 0 : return nlohmann::json();
54 : }
55 248 : auto j = nlohmann::json::parse(response_->get_content(), nullptr, false);
56 248 : if (j.is_discarded()) {
57 0 : return nlohmann::json();
58 : }
59 248 : return j;
60 248 : }
61 :
62 : // Status info
63 208 : int status() const {
64 208 : return response_ ? static_cast<int>(response_->get_status()) : 0;
65 : }
66 :
67 30 : int status_code() const { return status(); }
68 :
69 15 : bool is_redirect() const {
70 15 : 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 67 : std::string header(const std::string& key) const {
83 67 : return response_ && response_->has_header(key) ?
84 201 : response_->get_header(key) : "";
85 : }
86 :
87 6 : bool has_header(const std::string& key) const {
88 6 : return response_ && response_->has_header(key);
89 : }
90 :
91 : // Error info
92 0 : std::string error() const {
93 0 : if (error_) {
94 0 : return error_.message();
95 0 : } else if (!response_) {
96 0 : return "No response received";
97 0 : } else if (has_http_error()) {
98 0 : return "HTTP error " + std::to_string(status());
99 : }
100 0 : return "";
101 : }
102 :
103 : std::string error_message() const { return error(); }
104 :
105 : 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 : 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
|