@@ -47,46 +47,83 @@ class HttpResponse {
4747};
4848
4949// HttpRequest will handle all the headers and request params
50- class HttpRequest {
50+ //
51+ // Curiously Recurring Template Pattern (CRTP)
52+ //
53+ // POST/PUT must a have a body member variable that GET/HEAD don't
54+ // we want to do this at compile-time, however, because we are using a fluent
55+ // builder pattern, if we were to do regular inheritance, when using .body() we
56+ // would return a `&HttpBodyRequest : public HttpRequest`, which would not allow
57+ // us to chain useful methods on the HttpRequest class such as .timeout()
58+
59+ template <typename T> class HttpRequestBase {
5160public:
52- HttpRequest (HttpClient &client, std::string URL,
53- const HttpMethod &http_method)
61+ HttpRequestBase (HttpClient &client, std::string URL,
62+ const HttpMethod &http_method)
5463 : client_(client), URL_(std::move(URL)),
5564 http_method_ (std::move(http_method)), timeout_(0 ) {};
5665
57- HttpRequest &timeout (const long long &seconds) {
66+ T &timeout (const long long &seconds) {
5867 timeout_ = std::chrono::seconds (seconds);
59- return *this ;
68+ return static_cast <T &>( *this ) ;
6069 }
61- HttpRequest &timeout (const std::chrono::seconds &seconds) {
70+ T &timeout (const std::chrono::seconds &seconds) {
6271 timeout_ = seconds;
63- return *this ;
72+ return static_cast <T &>( *this ) ;
6473 }
65- HttpRequest &header (const std::string &header_, const std::string &value) {
74+ T &header (const std::string &header_, const std::string &value) {
6675 headers_[header_] = value;
67- return *this ;
76+ return static_cast <T &>( *this ) ;
6877 }
6978
70- HttpResponse execute ();
71-
7279 const std::string &getURL () const { return URL_; }
7380 const long long getTimeout () const { return timeout_.count (); }
7481 const std::unordered_map<std::string, std::string> &getHeaders () const {
7582 return headers_;
7683 }
7784
78- private :
85+ protected :
7986 HttpClient &client_;
8087 std::string URL_;
8188 std::unordered_map<std::string, std::string> headers_;
8289 std::chrono::seconds timeout_;
8390 HttpMethod http_method_;
8491};
8592
93+ // GET/HEAD
94+ class HttpRequest : public HttpRequestBase <HttpRequest> {
95+ public:
96+ using HttpRequestBase::HttpRequestBase;
97+ HttpResponse execute ();
98+ };
99+
100+ // POST/PUT
101+ class HttpBodyRequest : public HttpRequestBase <HttpBodyRequest> {
102+ public:
103+ HttpBodyRequest (HttpClient &client, std::string URL,
104+ const HttpMethod &http_method)
105+ : HttpRequestBase(client, std::move(URL), http_method) {}
106+
107+ HttpBodyRequest &body (const std::string &data) {
108+ body_ = std::move (data);
109+ return (*this );
110+ }
111+
112+ const std::string &getBody () const { return body_; }
113+
114+ HttpResponse execute ();
115+
116+ private:
117+ std::string body_;
118+ };
119+
86120// HttpClient should only focus on handling the cURL handle
87121// and making the request (HttpRequest) and returning HttpResponse
88122class HttpClient {
89- friend class HttpRequest ; // `execute()` is invoked from the request only
123+ // `execute()` is invoked from the request only
124+ friend class HttpRequest ;
125+ friend class HttpBodyRequest ;
126+
90127public:
91128 HttpClient () {
92129 curl_handle = curl_easy_init ();
@@ -131,6 +168,9 @@ class HttpClient {
131168 [[nodiscard]] HttpRequest head (const std::string &URL) {
132169 return HttpRequest{*this , URL, HttpMethod::Head};
133170 };
171+ [[nodiscard]] HttpBodyRequest post (const std::string &URL) {
172+ return HttpBodyRequest{*this , URL, HttpMethod::Post};
173+ };
134174
135175private:
136176 CURL *curl_handle = nullptr ;
@@ -146,6 +186,7 @@ class HttpClient {
146186 // this is invoked by HttpRequest
147187 HttpResponse execute_get (HttpRequest &request);
148188 HttpResponse execute_head (HttpRequest &request);
189+ HttpResponse execute_post (HttpBodyRequest &request);
149190
150191 const std::unordered_map<std::string, std::string> &getHeaders () const {
151192 return headers_;
0 commit comments