-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathclient.cpp
186 lines (164 loc) · 6.62 KB
/
client.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include "Utility.h"
#include <curl/curl.h>
#include <sstream>
#include <iostream>
#include <cstdlib>
namespace ThorsAnvil
{
namespace Socket
{
class CurlGlobal
{
public:
CurlGlobal()
{
if (curl_global_init(CURL_GLOBAL_ALL) != 0)
{
throw std::runtime_error(buildErrorMessage("CurlGlobal::", __func__, ": curl_global_init: fail"));
}
}
~CurlGlobal()
{
curl_global_cleanup();
}
};
extern "C" size_t curlConnectorGetData(char *ptr, size_t size, size_t nmemb, void *userdata);
enum RequestType {Get, Head, Put, Post, Delete};
class CurlConnector
{
CURL* curl;
std::string host;
int port;
std::string response;
friend size_t curlConnectorGetData(char *ptr, size_t size, size_t nmemb, void *userdata);
std::size_t getData(char *ptr, size_t size)
{
response.append(ptr, size);
return size;
}
template<typename Param, typename... Args>
void curlSetOptionWrapper(CURLoption option, Param parameter, Args... errorMessage)
{
CURLcode res;
if ((res = curl_easy_setopt(curl, option, parameter)) != CURLE_OK)
{
throw std::runtime_error(buildErrorMessage(errorMessage..., curl_easy_strerror(res)));
}
}
public:
CurlConnector(std::string const& host, int port)
: curl(curl_easy_init( ))
, host(host)
, port(port)
{
if (curl == nullptr)
{
throw std::runtime_error(buildErrorMessage("CurlConnector::", __func__, ": curl_easy_init: fail"));
}
}
CurlConnector(CurlConnector&) = delete;
CurlConnector& operator=(CurlConnector&) = delete;
CurlConnector(CurlConnector&& rhs) noexcept
: curl(nullptr)
{
rhs.swap(*this);
}
CurlConnector& operator=(CurlConnector&& rhs) noexcept
{
rhs.swap(*this);
return *this;
}
void swap(CurlConnector& other) noexcept
{
using std::swap;
swap(curl, other.curl);
swap(host, other.host);
swap(port, other.port);
swap(response, other.response);
}
~CurlConnector()
{
if (curl)
{
curl_easy_cleanup(curl);
}
}
virtual RequestType getRequestType() const = 0;
void sendMessage(std::string const& urlPath, std::string const& message)
{
if (curl == nullptr)
{
throw std::logic_error(buildErrorMessage("CurlConnector::", __func__, ": bad object (this object was moved)"));
}
std::stringstream url;
response.clear();
url << "http://" << host;
if (port != 80)
{
url << ":" << port;
}
url << urlPath;
CURLcode res;
auto sListDeleter = [](struct curl_slist* headers){curl_slist_free_all(headers);};
std::unique_ptr<struct curl_slist, decltype(sListDeleter)> headers(nullptr, sListDeleter);
headers = std::unique_ptr<struct curl_slist, decltype(sListDeleter)>(curl_slist_append(headers.get(), "Content-Type: text/text"), sListDeleter);
curlSetOptionWrapper(CURLOPT_HTTPHEADER, headers.get(), "CurlConnector::", __func__, ": curl_easy_setopt CURLOPT_HTTPHEADER:");
curlSetOptionWrapper(CURLOPT_ACCEPT_ENCODING, "*/*", "CurlConnector::", __func__, ": curl_easy_setopt CURLOPT_ACCEPT_ENCODING:");
curlSetOptionWrapper(CURLOPT_USERAGENT, "ThorsCurl-Client/0.1", "CurlConnector::", __func__, ": curl_easy_setopt CURLOPT_USERAGENT:");
curlSetOptionWrapper(CURLOPT_URL, url.str().c_str(), "CurlConnector::", __func__, ": curl_easy_setopt CURLOPT_URL:");
curlSetOptionWrapper(CURLOPT_POSTFIELDSIZE, message.size(), "CurlConnector::", __func__, ": curl_easy_setopt CURLOPT_POSTFIELDSIZE:");
curlSetOptionWrapper(CURLOPT_COPYPOSTFIELDS, message.data(), "CurlConnector::", __func__, ": curl_easy_setopt CURLOPT_COPYPOSTFIELDS:");
curlSetOptionWrapper(CURLOPT_WRITEFUNCTION, curlConnectorGetData, "CurlConnector::", __func__, ": curl_easy_setopt CURLOPT_WRITEFUNCTION:");
curlSetOptionWrapper(CURLOPT_WRITEDATA, this, "CurlConnector::", __func__, ": curl_easy_setopt CURLOPT_WRITEDATA:");
switch(getRequestType())
{
case Get: res = CURLE_OK; /* The default is GET. So do nothing.*/ break;
case Head: res = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "HEAD"); break;
case Put: res = curl_easy_setopt(curl, CURLOPT_PUT, 1); break;
case Post: res = curl_easy_setopt(curl, CURLOPT_POST, 1); break;
case Delete: res = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); break;
default:
throw std::domain_error(buildErrorMessage("CurlConnector::", __func__, ": invalid method: ", static_cast<int>(getRequestType())));
}
if (res != CURLE_OK)
{
throw std::runtime_error(buildErrorMessage("CurlConnector::", __func__, ": curl_easy_setopt CURL_METHOD:", curl_easy_strerror(res)));
}
if ((res = curl_easy_perform(curl)) != CURLE_OK)
{
throw std::runtime_error(buildErrorMessage("CurlConnector::", __func__, ": curl_easy_perform:", curl_easy_strerror(res)));
}
}
void recvMessage(std::string& message)
{
message = std::move(response);
}
};
class CurlPost: public CurlConnector
{
public:
using CurlConnector::CurlConnector;
virtual RequestType getRequestType() const {return Post;}
};
size_t curlConnectorGetData(char *ptr, size_t size, size_t nmemb, void *userdata)
{
CurlConnector* self = reinterpret_cast<CurlConnector*>(userdata);
return self->getData(ptr, size * nmemb);
}
}
}
int main(int argc, char* argv[])
{
namespace Sock = ThorsAnvil::Socket;
if (argc != 3)
{
std::cerr << "Usage: client <host> <Message>\n";
std::exit(1);
}
Sock::CurlGlobal curlInit;
Sock::CurlPost connect(argv[1], 8080);
connect.sendMessage("/message", argv[2]);
std::string message;
connect.recvMessage(message);
std::cout << message << "\n";
}