-
Notifications
You must be signed in to change notification settings - Fork 0
/
httpRequest.h
540 lines (441 loc) · 17.3 KB
/
httpRequest.h
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
#ifndef _HttpRequest_h_
#define _HttpRequest_h_
/*
The MIT License
Copyright (c) 2019 Kunal Ekawde
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "httpClientCommon.h"
#include <unistd.h>
#include <stdlib.h>
#include <exception>
#include <atomic>
#include <future>
extern int loglevel;
namespace base {
class HttpCallBackIf;
}
using HttpResponseReadyCallbackSharedPtr = std::shared_ptr<base::HttpCallBackIf>;
using HttpResponseReadyCallbackWeakPtr = std::weak_ptr<base::HttpCallBackIf>;
namespace base
{
typedef enum
{
invalid_c=0,
get_c=1,
post_c=2,
put_c=3,
del_c=4,
patch_c=5,
head_c=6,
trace_c=7,
options_c=8,
connect_c=9,
maxtype_c
}CniMsgType;
enum TypeSocket {BlockingSocket, NonBlockingSocket};
class HttpCookie
{
public:
HttpCookie():transId_m(++cookie_m) {}
~HttpCookie(){}
const HttpCookie& operator=(const HttpCookie& other)=delete;
const uint32_t getId() const {
return transId_m;
}
private:
static std::atomic<uint32_t> cookie_m;
uint32_t transId_m=0;
};
using HttpCookieShPtr = std::shared_ptr<base::HttpCookie>;
class IpAddress
{
public:
IpAddress(){}
IpAddress(uint32_t ip):ip_m(ip) {}
~IpAddress(){}
std::string toString() {
in_addr inaddr;
inaddr.s_addr = ip_m;
return inet_ntoa(inaddr);
}
uint32_t getV4Address() { return ip_m;}
private:
uint32_t ip_m;
};
class TcpSocket
{
public:
TcpSocket() {sockfd_m=0;open_m=false;}
TcpSocket(IpAddress& ipA):TcpSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, ipA.getV4Address()) {
}
TcpSocket(uint32_t domain, uint32_t type, uint32_t prot, uint32_t ipv4): open_m(false) {
if (!this->open(domain, type, prot)) {
throw std::exception();
}
int32_t set = 1;
if (setsockopt(sockfd_m, SOL_SOCKET, SO_REUSEADDR, (char*)&set, sizeof(set)) == -1) {
throw std::exception();
}
uint16_t port = 0;
struct sockaddr_storage __ss;
struct sockaddr_in* localAddr;
localAddr = (struct sockaddr_in*) &__ss;
localAddr->sin_family = AF_INET;
localAddr->sin_addr.s_addr = ipv4;
localAddr->sin_port = htons(port);
if (bind(sockfd_m, (struct sockaddr*)&__ss, sizeof(__ss)) < 0) {
// In order to meet the strong guarantee close the socket.
this->close();
throw std::exception();
}
}
bool open(uint32_t domain, uint32_t type, uint32_t prot) {
if (open_m) {
//do not open again so we do not overwrite fd
return true;
}
if ((sockfd_m = socket(domain, type, prot)) < 0) {
return false;
}
//Socket Options feilds
blocking_m = BlockingSocket;
//Socket is open
open_m = true;
return true;
}
bool close() {
int32_t ret = 0;
do {
ret = ::close(sockfd_m);
}
while (ret < 0 && errno == EINTR);
if (ret < 0) {
return false;
}
//FD is closed
sockfd_m = -1;
open_m = false;
return true;
}
~TcpSocket(){}
bool setNoDelay(int32_t noDelay) {
int flag = noDelay;
if (setsockopt(sockfd_m, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(int)) == -1)
return false;
return true;
}
int32_t getSocketFd() {
return sockfd_m;
}
private:
int32_t sockfd_m;
bool open_m;
TypeSocket blocking_m;
};
enum class HttpTransferFlag
{
HTTP_11 = 0,
HTTP2_PRIOR_KNOWLEDGE = 1
};
enum class Http2StreamDependency
{
HTTP_STREAM_DEP_INVALID = 0,
HTTP_STREAM_DEP_NORMAL = 1,
HTTP_STREAM_DEP_EXCLUSIVE = 2,
};
/**
HttpRequest used to send HTTP request and received HTTP response.
*/
class HttpRequest : public std::enable_shared_from_this<HttpRequest>
{
public:
HttpRequest();
virtual ~HttpRequest();
void SetRequestHeaders(const std::unordered_map<std::string, std::string>& headers);
void AddRequestHeaders(const std::unordered_map<std::string, std::string>& headers);
void AddRequestHeader(const std::string& field, const std::string& value);
const std::multimap<std::string, std::string>& GetResponseHeaders() const;
void SetRequestTimeoutInSec(uint16_t time);
void SetRequestTimeoutInMilliSec(unsigned long time);
typedef std::function<
bool(const std::shared_ptr<HttpRequest>& connection,
char* body,
std::size_t expected_length,
std::size_t& actual_length)
> ReadBodyCallback;
typedef std::function<
bool(const std::shared_ptr<HttpRequest>& connection,
const char* header,
std::size_t length)
> WriteHeaderCallback;
typedef std::function<
bool(const std::shared_ptr<HttpRequest>& connection,
const char* body,
std::size_t length)
> WriteBodyCallback;
enum class DebugDataType {
Information,
ReceivedHeader,
SentHeader,
ReceivedBody,
SentBody,
ReceivedSslData,
SentSslData,
};
typedef std::function<
void(const std::shared_ptr<HttpRequest>& connection,
DebugDataType data_type,
const char* data,
std::size_t size)
> DebugCallback;
typedef std::function<void(const std::shared_ptr<HttpRequest>& connection)> FinishedCallback;
virtual void Start();
void SetVerbose(bool verbose);
void SetUrl(const std::string& url);
void SetDnsResolveItems(const std::multimap<std::string, std::string>& resolve_items);
void SetDnsCacheTimeOut(uint32_t secs);
void SetRequestBody(const std::shared_ptr<std::string>& body) {
if(body != nullptr)
requestBody_m = body;
}
std::shared_ptr<std::string> GetRequestBody() {
return requestBody_m;
}
void SetReceiveBody(bool receive_body);
void SetConnectTimeoutInMilliseconds(long milliseconds);
void SetTimeoutInMilliseconds(long milliseconds);
void SetReadBodyCallback(const ReadBodyCallback& callback) {
readBodyCallback_m = callback;
}
void SetWriteHeaderCallback(const WriteHeaderCallback& callback) {
writeHeaderCallback_m = callback;
}
void SetWriteBodyCallback(const WriteBodyCallback& callback) {
writeBodyCallback_m = callback;
}
void SetDebugCallback(const DebugCallback& callback);
void SetFinishedCallback(const FinishedCallback& callback) {
finishedCallback_m = callback;
}
void SetAppResponseCallback(const HttpResponseReadyCallbackSharedPtr& callback = nullptr) {
appResponseCallback_m = callback;
}
HttpResponseReadyCallbackWeakPtr GetAppResponseCallback() {
return appResponseCallback_m;
}
CURLcode GetResult() const {
return result_m;
}
long GetResponseCode() const;
long GetResponseCode(CURLcode cc) const;
const std::string& GetResponseHeader() const {
return responseHeader_m;
}
void ResetResponseHeader() {
responseHeader_m.clear();
}
const std::string& GetResponseBody() const {
return responseBody_m;
}
CURL* GetHandle() const {
return handle_mp;
}
std::string& GetUrl() {
return requestUrl_m;
}
void SetCookie(const std::shared_ptr<base::HttpCookie>& cookie) {
cookie_m = cookie;
}
std::shared_ptr<base::HttpCookie> GetCookie() {
return cookie_m;
}
virtual void SetHttpMethod(base::CniMsgType type);
virtual base::CniMsgType GetHttpMethod() {
return httpMethod_m;
}
void SetTcpKeepAlive(bool enabled=true, uint32_t keepAliveIdleTime=60, uint32_t keepAliveInterval=60);
void SetPodIpV4(uint32_t ip) { podIpV4_m = ip; }
void SetPodIpV4(std::string ipAdr) {
unsigned char buf[sizeof(struct in6_addr)];
int s = inet_pton(AF_INET, ipAdr.c_str(), buf);
if (s <= 0) {
if (s == 0)
LOG_ERROR("Not in presentation format");
else
perror("inet_pton");
exit(EXIT_FAILURE);
}
podIpV4_m = s;
}
void SetPostSize(long size);
void SetUpLoadHttpBody(bool enabled);
void SetHTTP2Transfer(base::HttpTransferFlag flag) {
httpTransferFlag_m = flag;
}
void SetHTTP2Transfer() {
curl_easy_setopt(GetHandle(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
curl_easy_setopt(GetHandle(), CURLOPT_PIPEWAIT, 1L);
}
base::HttpTransferFlag GetHTTP2Transfer() {
return httpTransferFlag_m;
}
bool IsSetWriteBodyCallback() const {
return (writeBodyCallback_m)?true:false;
}
const uint32_t getCookieId() {
return GetCookie()->getId();
}
std::promise<int>& Promise() {return response_m;}
private:
void InitStart();
void Finished(CURLcode result);
void CheckAndSetUpHttpBodyUpload();
protected:
virtual void ResetResponseStates();
virtual void ResetResponseStatesB();
private:
static size_t CurlReadBodyCallback(char* buffer, size_t size, size_t nitems, void* instream);
static size_t CurlWriteHeaderCallback(char* buffer, size_t size, size_t nitems, void* userdata);
static size_t CurlWriteBodyCallback(char* ptr, size_t size, size_t nmemb, void* v);
static int CurlDebugCallback(CURL* handle,
curl_infotype type,
char* data,
size_t size,
void* userptr);
void SetInitialOptions();
void SetSharedHandle(bool enable=true);
void ReleaseDnsResolveItems();
bool ReadBody(char* body, std::size_t expected_length, std::size_t& actual_length);
bool WriteHeader(const char* header, std::size_t length);
bool WriteBody(const char* body, std::size_t length);
void Debug(DebugDataType data_type, const char* data, std::size_t size);
const char* GetResultString() const { return errorBuff_m; }
const char* GetEffectiveUrl() const;
private:
static curl_socket_t CurlOpenSocketCallback(void* clientp, curlsocktype socket_type, curl_sockaddr* address);
static int CurlCloseSocketCallback(void* clientp, curl_socket_t socket);
curl_socket_t OpenSocket(curlsocktype socket_type, curl_sockaddr* address);
bool CloseSocket(curl_socket_t socket);
private:
CURL* handle_mp;
bool isRunning_m;
char errorBuff_m[CURL_ERROR_SIZE]{0};
curl_slist* dnsResolveItems_mp;
std::size_t requestBodyReadLength_m;
ReadBodyCallback readBodyCallback_m;
WriteHeaderCallback writeHeaderCallback_m;
WriteBodyCallback writeBodyCallback_m;
DebugCallback debugCallback_m;
FinishedCallback finishedCallback_m;
HttpResponseReadyCallbackWeakPtr appResponseCallback_m;
CURLcode result_m;
std::string responseHeader_m;
std::string responseBody_m;
std::string requestUrl_m;
std::shared_ptr<base::HttpCookie> cookie_m;
std::shared_ptr<std::string> requestBody_m;
std::shared_ptr<base::TcpSocket> tcpsock_m{nullptr};
uint32_t podIpV4_m{0};
base::CniMsgType httpMethod_m;
base::HttpTransferFlag httpTransferFlag_m;
friend class HttpClientManager;
private:
void ParseResponseHeaders() const;
void ReleaseRequestHeaders();
private:
curl_slist* requestHeaders_mp;
mutable bool hasParsedResponseHeaders_m;
mutable std::multimap<std::string, std::string> responseHeaders_m;
std::promise<int> response_m;
};
class HttpClientResponse
{
public:
explicit HttpClientResponse(const uint32_t tid, long rc, std::string&& hdrs, std::shared_ptr<std::string> buffPtr):tid_m(tid),responseCode_m(rc),httpRspHdrs_m(hdrs),httpRspBufferPtr_m(buffPtr) {
}
~HttpClientResponse() = default;
const uint32_t getTid() {
return tid_m;
}
std::string& getHttpRspHdrs() {
return httpRspHdrs_m;
}
std::shared_ptr<std::string> getHttpRspBuffer() {
return httpRspBufferPtr_m;
}
long getRespCode() {
return responseCode_m;
}
private:
uint32_t tid_m;
long responseCode_m;
std::string httpRspHdrs_m;
std::shared_ptr<std::string> httpRspBufferPtr_m;
};
using HttpClientRspPtr = std::shared_ptr<HttpClientResponse>;
class HttpCallBackIf
{
public:
HttpCallBackIf() {};
virtual ~HttpCallBackIf() {};
virtual void operator()(const HttpClientRspPtr& httpResp,
HttpClientStatusCode sc)
{};
private:
HttpCallBackIf(const HttpCallBackIf &)=delete;
const HttpCallBackIf& operator=(const HttpCallBackIf &)=delete;
};
template <typename T1>
class HttpAsyncCallBack : public HttpCallBackIf
{
public:
HttpAsyncCallBack(T1 *class_p,
void(T1::*method_p)( const HttpClientRspPtr& httpResp,
HttpClientStatusCode sc))
: class_mp(class_p), method_mp(method_p)
{
if ( (class_mp == 0) || (method_mp == 0) )
{
throw std::exception();
}
};
virtual ~HttpAsyncCallBack() {};
virtual void operator()(const HttpClientRspPtr& httpResp,
HttpClientStatusCode sc)
{
if ((class_mp != nullptr) && (method_mp != nullptr))
{
(*class_mp.*method_mp)(std::move(httpResp), std::move(sc));
}
else
{
throw std::exception();
}
};
private:
// Pointer to the type
T1 *class_mp;
// Pointer to the callback function.
void (T1::*method_mp) (const HttpClientRspPtr& httpResp,
HttpClientStatusCode sc);
};
}
#endif