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
|
// UrlClient.h
// Declares the cUrlClient class for high-level URL interaction
/*
Options that can be set via the Options parameter to the cUrlClient calls:
"MaxRedirects": The maximum number of allowed redirects before the client refuses a redirect with an error
"OwnCert": The client certificate to use, if requested by the server. Any string that can be parsed by cX509Cert.
"OwnPrivKey": The private key appropriate for OwnCert. Any string that can be parsed by cCryptoKey.
"OwnPrivKeyPassword": The password for OwnPrivKey. If not present or empty, no password is assumed.
"TrustedRootCAs": The trusted root CA certificates (\n-delimited concatenated PEM format) to be used for peer cert verification. If not present, peer cert is not verified.
Behavior:
- If a redirect is received, and redirection is allowed, the redirection is reported via OnRedirecting() callback
and the request is restarted at the redirect URL, without reporting any of the redirect's headers nor body
- If a redirect is received and redirection is not allowed (maximum redirection attempts have been reached),
the OnRedirecting() callback is called with the redirect URL and then the request terminates with an OnError() callback,
without reporting the redirect's headers nor body.
*/
#pragma once
#include "../OSSupport/Network.h"
class cUrlClient
{
public:
/** Callbacks that are used for progress and result reporting. */
class cCallbacks
{
public:
// Force a virtual destructor in descendants:
virtual ~cCallbacks() {}
/** Called when the TCP connection is established. */
virtual void OnConnected(cTCPLink & a_Link) {}
/** Called for TLS connections, when the server certificate is received.
Return true to continue with the request, false to abort.
The default implementation does nothing and continues with the request.
TODO: The certificate parameter needs a representation! */
virtual bool OnCertificateReceived() { return true; }
/** Called for TLS connections, when the TLS handshake has been completed.
An empty default implementation is provided so that clients don't need to reimplement it unless they are interested in the event. */
virtual void OnTlsHandshakeCompleted() { }
/** Called after the entire request has been sent to the remote peer. */
virtual void OnRequestSent() {}
/** Called after the first line of the response is parsed, unless the response is an allowed redirect. */
virtual void OnStatusLine(const AString & a_HttpVersion, int a_StatusCode, const AString & a_Rest) {}
/** Called when a single HTTP header is received and parsed, unless the response is an allowed redirect
Called once for each incoming header. */
virtual void OnHeader(const AString & a_Key, const AString & a_Value) {}
/** Called when the HTTP headers have been fully parsed, unless the response is an allowed redirect.
There will be no more OnHeader() calls. */
virtual void OnHeadersFinished() {}
/** Called when the next fragment of the response body is received, unless the response is an allowed redirect.
This can be called multiple times, as data arrives over the network. */
virtual void OnBodyData(const void * a_Data, size_t a_Size) {}
/** Called after the response body has been fully reported by OnBody() calls, unless the response is an allowed redirect.
There will be no more OnBody() calls. */
virtual void OnBodyFinished() {}
/** Called when an asynchronous error is encountered. */
virtual void OnError(const AString & a_ErrorMsg) {}
/** Called when a redirect is to be followed.
This is called even if the redirecting is prohibited by the options; in such an event, this call will be
followed by OnError().
If a response indicates a redirect (and the request allows redirecting), the regular callbacks
OnStatusLine(), OnHeader(), OnHeadersFinished(), OnBodyData() and OnBodyFinished() are not called
for such a response; instead, the redirect is silently attempted. */
virtual void OnRedirecting(const AString & a_NewLocation) {}
};
using cCallbacksPtr = std::unique_ptr<cCallbacks>;
/** Used for HTTP status codes. */
enum eHTTPStatus
{
HTTP_STATUS_OK = 200,
HTTP_STATUS_MULTIPLE_CHOICES = 300, // MAY have a redirect using the "Location" header
HTTP_STATUS_MOVED_PERMANENTLY = 301, // redirect using the "Location" header
HTTP_STATUS_FOUND = 302, // redirect using the "Location" header
HTTP_STATUS_SEE_OTHER = 303, // redirect using the "Location" header
HTTP_STATUS_TEMPORARY_REDIRECT = 307, // redirect using the "Location" header
};
/** Makes a network request to the specified URL, using the specified method (if applicable).
The response is reported via the a_ResponseCallback callback, in a single call.
The metadata about the response (HTTP headers) are reported via a_InfoCallback before the a_ResponseCallback call.
If there is an asynchronous error, it is reported in via the a_ErrorCallback.
If there is an immediate error (misformatted URL etc.), the function returns false and an error message.
a_Headers contains additional headers to use for the request.
a_Body specifies optional body to include with the request, if applicable.
a_Options contains various options for the request that govern the request behavior, but aren't sent to the server,
such as the proxy server, whether to follow redirects, and client certificate for TLS. */
static std::pair<bool, AString> Request(
const AString & a_Method,
const AString & a_URL,
cCallbacksPtr && a_Callbacks,
AStringMap && a_Headers,
const AString & a_Body,
const AStringMap & a_Options
);
/** Alias for Request("GET", ...) */
static std::pair<bool, AString> Get(
const AString & a_URL,
cCallbacksPtr && a_Callbacks,
AStringMap && a_Headers = {},
const AString & a_Body = {},
const AStringMap & a_Options = {}
);
/** Alias for Request("POST", ...) */
static std::pair<bool, AString> Post(
const AString & a_URL,
cCallbacksPtr && a_Callbacks,
AStringMap && a_Headers,
const AString & a_Body,
const AStringMap & a_Options = {}
);
/** Alias for Request("PUT", ...) */
static std::pair<bool, AString> Put(
const AString & a_URL,
cCallbacksPtr && a_Callbacks,
AStringMap && a_Headers,
const AString & a_Body,
const AStringMap & a_Options = {}
);
/** The method will run a thread blocking HTTP request. Any error handling
is done inside the functions. Check the LOG or stdout for any occurring
errors. Other parameters are the same as for the regular request method.
The return value is if the request was successful and the response. */
static std::pair<bool, AString> BlockingRequest(
const AString & a_Method,
const AString & a_URL,
AStringMap && a_Headers = {},
const AString & a_Body = {},
const AStringMap & a_Options = {}
);
/** Alias for BlockingRequest("GET", ...) */
static std::pair<bool, AString> BlockingGet(
const AString & a_URL,
AStringMap a_Headers = {},
const AString & a_Body = {},
const AStringMap & a_Options = {}
);
/** Alias for BlockingRequest("POST", ...) */
static std::pair<bool, AString> BlockingPost(
const AString & a_URL,
AStringMap && a_Headers,
const AString & a_Body,
const AStringMap & a_Options = {}
);
/** Alias for BlockingRequest("PUT", ...) */
static std::pair<bool, AString> BlockingPut(
const AString & a_URL,
AStringMap && a_Headers,
const AString & a_Body,
const AStringMap & a_Options = {}
);
};
|