1
1
#include < tdme/network/httpclient/HTTPDownloadClient.h>
2
2
3
+ #include < iomanip>
3
4
#include < memory>
4
5
#include < filesystem>
5
6
#include < fstream>
6
7
#include < string>
8
+ #include < sstream>
9
+ #include < unordered_map>
7
10
#include < vector>
8
11
9
12
#include < tdme/tdme.h>
24
27
#include < tdme/utilities/StringTokenizer.h>
25
28
#include < tdme/utilities/StringTools.h>
26
29
30
+ using std::hex;
27
31
using std::make_unique;
32
+ using std::nouppercase;
28
33
using std::ifstream;
29
34
using std::ios;
30
35
using std::ofstream;
36
+ using std::ostringstream;
37
+ using std::setw;
31
38
using std::string;
32
39
using std::to_string;
33
40
using std::unique_ptr;
41
+ using std::unordered_map;
42
+ using std::uppercase;
34
43
using std::vector;
35
44
36
45
using tdme::math::Math;
@@ -55,9 +64,39 @@ using tdme::network::httpclient::HTTPDownloadClient;
55
64
HTTPDownloadClient::HTTPDownloadClient (): downloadThreadMutex(" downloadthread-mutex" ) {
56
65
}
57
66
67
+ string HTTPDownloadClient::urlEncode (const string &value) {
68
+ // TODO: put me into utilities
69
+ // see: https://stackoverflow.com/questions/154536/encode-decode-urls-in-c
70
+ ostringstream escaped;
71
+ escaped.fill (' 0' );
72
+ escaped << hex;
73
+
74
+ for (string::const_iterator i = value.begin (), n = value.end (); i != n; ++i) {
75
+ string::value_type c = (*i);
76
+
77
+ // Keep alphanumeric and other accepted characters intact
78
+ if (Character::isAlphaNumeric (c) == true || c == ' -' || c == ' _' || c == ' .' || c == ' ~' ) {
79
+ escaped << c;
80
+ continue ;
81
+ }
82
+
83
+ // Any other characters are percent-encoded
84
+ escaped << uppercase;
85
+ escaped << ' %' << setw (2 ) << int ((unsigned char ) c);
86
+ escaped << nouppercase;
87
+ }
88
+
89
+ return escaped.str ();
90
+ }
91
+
58
92
string HTTPDownloadClient::createHTTPRequestHeaders (const string& hostName, const string& relativeUrl) {
93
+ string query;
94
+ for (const auto & [parameterName, parameterValue]: getParameters) {
95
+ if (query.empty () == true ) query+= " ?" ; else query+=" &" ;
96
+ query+= urlEncode (parameterName) + " =" + urlEncode (parameterValue);
97
+ }
59
98
auto request =
60
- string (" GET " + relativeUrl + " HTTP/1.1\r\n " ) +
99
+ string (" GET " + relativeUrl + query + " HTTP/1.1\r\n " ) +
61
100
string (" User-Agent: tdme2-httpdownloadclient\r\n " ) +
62
101
string (" Host: " + hostName + " \r\n " ) +
63
102
string (" Connection: close\r\n " );
@@ -66,24 +105,36 @@ string HTTPDownloadClient::createHTTPRequestHeaders(const string& hostName, cons
66
105
Base64::encode (username + " :" + password, base64Pass);
67
106
request+= " Authorization: Basic " + base64Pass + " \r\n " ;
68
107
}
108
+ for (const auto & [headerName, headerValue]: headers) {
109
+ request+= headerName + " : " + headerValue + " \r\n " ;
110
+ }
69
111
request+=
70
112
string (" \r\n " );
71
113
return request;
72
114
}
73
115
74
- uint64_t HTTPDownloadClient::parseHTTPResponseHeaders (ifstream& rawResponse, int16_t & httpStatusCode, vector<string>& httpHeader) {
75
- httpHeader.clear ();
116
+ uint64_t HTTPDownloadClient::parseHTTPResponseHeaders (ifstream& rawResponse, int16_t & statusCode, unordered_map<string, string>& responseHeaders) {
117
+ responseHeaders.clear ();
118
+ auto headerSize = 0ll ;
119
+ auto returnHeaderSize = 0ll ;
120
+ int headerIdx = 0 ;
121
+ string statusHeader;
76
122
string line;
77
- uint64_t headerSize = 0 ;
78
- uint64_t returnHeaderSize = 0 ;
79
123
char lastChar = -1 ;
80
124
char currentChar;
81
125
while (rawResponse.eof () == false ) {
82
126
rawResponse.get (currentChar);
83
127
headerSize++;
84
128
if (lastChar == ' \r ' && currentChar == ' \n ' ) {
85
129
if (line.empty () == false ) {
86
- httpHeader.push_back (line);
130
+ if (headerIdx == 0 ) {
131
+ statusHeader = line;
132
+ headerIdx++;
133
+ } else {
134
+ auto headerNameValueSeparator = StringTools::indexOf (line, ' :' );
135
+ responseHeaders[StringTools::trim (StringTools::substring (line, 0 , headerNameValueSeparator))] =
136
+ StringTools::trim (StringTools::substring (line, headerNameValueSeparator + 1 ));
137
+ }
87
138
} else {
88
139
returnHeaderSize = headerSize;
89
140
break ;
@@ -95,24 +146,28 @@ uint64_t HTTPDownloadClient::parseHTTPResponseHeaders(ifstream& rawResponse, int
95
146
}
96
147
lastChar = currentChar;
97
148
}
98
- if (httpHeader. size () > 0 ) {
149
+ if (statusHeader. empty () == false ) {
99
150
StringTokenizer t;
100
- t.tokenize (httpHeader[ 0 ] , " " );
151
+ t.tokenize (statusHeader , " " );
101
152
for (auto i = 0 ; i < 3 && t.hasMoreTokens (); i++) {
102
153
auto token = t.nextToken ();
103
154
if (i == 1 ) {
104
- httpStatusCode = Integer::parse (token);
155
+ statusCode = Integer::parse (token);
105
156
}
106
157
}
107
158
}
159
+ //
108
160
return returnHeaderSize;
109
161
}
110
162
111
163
void HTTPDownloadClient::reset () {
112
164
url.clear ();
113
165
file.clear ();
114
- httpStatusCode = -1 ;
115
- httpHeader.clear ();
166
+ headers.clear ();
167
+ getParameters.clear ();
168
+ statusCode = -1 ;
169
+ responseHeaders.clear ();
170
+ //
116
171
haveHeaders = false ;
117
172
haveContentSize = false ;
118
173
headerSize = 0LL ;
@@ -180,14 +235,14 @@ void HTTPDownloadClient::start() {
180
235
throw HTTPClientException (" Unable to open file for reading(" + to_string (errno) + " ): " + (downloadClient->file + " .download" ));
181
236
}
182
237
// try to read headers
183
- downloadClient->httpHeader .clear ();
184
- if ((downloadClient->headerSize = downloadClient->parseHTTPResponseHeaders (ifs, downloadClient->httpStatusCode , downloadClient->httpHeader )) > 0 ) {
238
+ downloadClient->responseHeaders .clear ();
239
+ if ((downloadClient->headerSize = downloadClient->parseHTTPResponseHeaders (ifs, downloadClient->statusCode , downloadClient->responseHeaders )) > 0 ) {
185
240
downloadClient->haveHeaders = true ;
186
- for ( const auto & header: downloadClient->httpHeader ) {
187
- if (StringTools::startsWith (header, " Content-Length: " ) == true ) {
188
- downloadClient-> haveContentSize = true ;
189
- downloadClient->contentSize = Integer::parse ( StringTools::substring (header, string ( " Content-Length: " ). size ())) ;
190
- }
241
+ auto contentLengthHeaderIt = downloadClient->responseHeaders . find ( " Content-Length " );
242
+ if (contentLengthHeaderIt != downloadClient-> responseHeaders . end () ) {
243
+ const auto & contentLengthHeader = contentLengthHeaderIt-> second ;
244
+ downloadClient->haveContentSize = true ;
245
+ downloadClient-> contentSize = Integer::parse (contentLengthHeader);
191
246
}
192
247
}
193
248
ifs.close ();
@@ -206,7 +261,7 @@ void HTTPDownloadClient::start() {
206
261
}
207
262
208
263
// transfer to real file
209
- if (downloadClient->httpStatusCode == 200 && isStopRequested () == false ) {
264
+ if (downloadClient->statusCode == 200 && isStopRequested () == false ) {
210
265
// input file stream
211
266
ifstream ifs (std::filesystem::u8path (downloadClient->file + " .download" ), ofstream::binary);
212
267
if (ifs.is_open () == false ) {
0 commit comments