diff options
Diffstat (limited to 'platform')
-rw-r--r-- | platform/http_client.cpp | 165 | ||||
-rw-r--r-- | platform/http_client.hpp | 236 | ||||
-rw-r--r-- | platform/http_client_apple.mm | 173 | ||||
-rw-r--r-- | platform/http_client_curl.cpp | 57 | ||||
-rw-r--r-- | platform/platform.pro | 1 | ||||
-rw-r--r-- | platform/platform_mac.mm | 3 |
6 files changed, 348 insertions, 287 deletions
diff --git a/platform/http_client.cpp b/platform/http_client.cpp new file mode 100644 index 0000000000..dcd1727b69 --- /dev/null +++ b/platform/http_client.cpp @@ -0,0 +1,165 @@ +#include "platform/http_client.hpp" + +#include "base/string_utils.hpp" + +#include "std/sstream.hpp" + +namespace platform +{ +HttpClient & HttpClient::SetDebugMode(bool debug_mode) +{ + m_debugMode = debug_mode; + return *this; +} + +HttpClient & HttpClient::SetUrlRequested(string const & url) +{ + m_urlRequested = url; + return *this; +} + +HttpClient & HttpClient::SetHttpMethod(string const & method) +{ + m_httpMethod = method; + return *this; +} + +HttpClient & HttpClient::SetBodyFile(string const & body_file, string const & content_type, + string const & http_method /* = "POST" */, + string const & content_encoding /* = "" */) +{ + m_inputFile = body_file; + m_bodyData.clear(); + m_contentType = content_type; + m_httpMethod = http_method; + m_contentEncoding = content_encoding; + return *this; +} + +HttpClient & HttpClient::SetReceivedFile(string const & received_file) +{ + m_outputFile = received_file; + return *this; +} + +HttpClient & HttpClient::SetUserAgent(string const & user_agent) +{ + m_userAgent = user_agent; + return *this; +} + +HttpClient & HttpClient::SetUserAndPassword(string const & user, string const & password) +{ + m_basicAuthUser = user; + m_basicAuthPassword = password; + return *this; +} + +HttpClient & HttpClient::SetCookies(string const & cookies) +{ + m_cookies = cookies; + return *this; +} + +HttpClient & HttpClient::SetHandleRedirects(bool handle_redirects) +{ + m_handleRedirects = handle_redirects; + return *this; +} + +string const & HttpClient::UrlRequested() const +{ + return m_urlRequested; +} + +string const & HttpClient::UrlReceived() const +{ + return m_urlReceived; +} + +bool HttpClient::WasRedirected() const +{ + return m_urlRequested != m_urlReceived; +} + +int HttpClient::ErrorCode() const +{ + return m_errorCode; +} + +string const & HttpClient::ServerResponse() const +{ + return m_serverResponse; +} + +string const & HttpClient::HttpMethod() const +{ + return m_httpMethod; +} + +string HttpClient::CombinedCookies() const +{ + if (m_serverCookies.empty()) + return m_cookies; + + if (m_cookies.empty()) + return m_serverCookies; + + return m_serverCookies + "; " + m_cookies; +} + +string HttpClient::CookieByName(string name) const +{ + string const str = CombinedCookies(); + name += "="; + auto const cookie = str.find(name); + auto const eq = cookie + name.size(); + if (cookie != string::npos && str.size() > eq) + return str.substr(eq, str.find(';', eq) - eq); + + return {}; +} + +// static +string HttpClient::NormalizeServerCookies(string && cookies) +{ + istringstream is(cookies); + string str, result; + + // Split by ", ". Can have invalid tokens here, expires= can also contain a comma. + while (getline(is, str, ',')) + { + size_t const leading = str.find_first_not_of(" "); + if (leading != string::npos) + str.substr(leading).swap(str); + + // In the good case, we have '=' and it goes before any ' '. + auto const eq = str.find('='); + if (eq == string::npos) + continue; // It's not a cookie: no valid key value pair. + + auto const sp = str.find(' '); + if (sp != string::npos && eq > sp) + continue; // It's not a cookie: comma in expires date. + + // Insert delimiter. + if (!result.empty()) + result.append("; "); + + // Read cookie itself. + result.append(str, 0, str.find(";")); + } + return result; +} + +string DebugPrint(HttpClient const & request) +{ + ostringstream ostr; + ostr << "HTTP " << request.ErrorCode() << " url [" << request.UrlRequested() << "]"; + if (request.WasRedirected()) + ostr << " was redirected to [" << request.UrlReceived() << "]"; + if (!request.ServerResponse().empty()) + ostr << " response: " << request.ServerResponse(); + return ostr.str(); +} +} diff --git a/platform/http_client.hpp b/platform/http_client.hpp index ad21a0409e..11f77084d4 100644 --- a/platform/http_client.hpp +++ b/platform/http_client.hpp @@ -25,212 +25,106 @@ SOFTWARE. #include "base/macros.hpp" -#include "std/sstream.hpp" #include "std/string.hpp" namespace platform { class HttpClient { - public: - enum { kNotInitialized = -1 }; +public: + static auto constexpr kNoError = -1; HttpClient() = default; HttpClient(string const & url) : m_urlRequested(url) {} - HttpClient & set_debug_mode(bool debug_mode) - { - m_debugMode = debug_mode; - return *this; - } - HttpClient & set_url_requested(string const & url) - { - m_urlRequested = url; - return *this; - } - HttpClient & set_http_method(string const & method) - { - m_httpMethod = method; - return *this; - } + + // Synchronous (blocking) call, should be implemented for each platform + // @returns true if connection was made and server returned something (200, 404, etc.). + // @note Implementations should transparently support all needed HTTP redirects. + // Implemented for each platform. + bool RunHttpRequest(); + + // Shared methods for all platforms, implemented at http_client.cpp + HttpClient & SetDebugMode(bool debug_mode); + HttpClient & SetUrlRequested(string const & url); + HttpClient & SetHttpMethod(string const & method); // This method is mutually exclusive with set_body_data(). - HttpClient & set_body_file(string const & body_file, - string const & content_type, - string const & http_method = "POST", - string const & content_encoding = "") - { - m_bodyFile = body_file; - m_bodyData.clear(); - m_contentType = content_type; - m_httpMethod = http_method; - m_contentEncoding = content_encoding; - return *this; - } + HttpClient & SetBodyFile(string const & body_file, string const & content_type, + string const & http_method = "POST", + string const & content_encoding = ""); // If set, stores server reply in file specified. - HttpClient & set_received_file(string const & received_file) - { - m_receivedFile = received_file; - return *this; - } - HttpClient & set_user_agent(string const & user_agent) - { - m_userAgent = user_agent; - return *this; - } - // This method is mutually exclusive with set_body_file(). - HttpClient & set_body_data(string const & body_data, - string const & content_type, - string const & http_method = "POST", - string const & content_encoding = "") - { - m_bodyData = body_data; - m_bodyFile.clear(); - m_contentType = content_type; - m_httpMethod = http_method; - m_contentEncoding = content_encoding; - return *this; - } - // Move version to avoid string copying. + HttpClient & SetReceivedFile(string const & received_file); + HttpClient & SetUserAgent(string const & user_agent); // This method is mutually exclusive with set_body_file(). - HttpClient & set_body_data(string && body_data, - string const & content_type, - string const & http_method = "POST", - string const & content_encoding = "") + template <typename StringT> + HttpClient & SetBodyData(StringT && body_data, string const & content_type, + string const & http_method = "POST", + string const & content_encoding = "") { - m_bodyData = move(body_data); - m_bodyFile.clear(); + m_bodyData = forward<StringT>(body_data); + m_inputFile.clear(); m_contentType = content_type; m_httpMethod = http_method; m_contentEncoding = content_encoding; return *this; } // HTTP Basic Auth. - HttpClient & set_user_and_password(string const & user, string const & password) - { - m_basicAuthUser = user; - m_basicAuthPassword = password; - return *this; - } + HttpClient & SetUserAndPassword(string const & user, string const & password); // Set HTTP Cookie header. - HttpClient & set_cookies(string const & cookies) - { - m_cookies = cookies; - return *this; - } + HttpClient & SetCookies(string const & cookies); // When set to true (default), clients never get 3XX codes from servers, redirects are handled automatically. // TODO: "false" is now supported on Android only. - HttpClient & set_handle_redirects(bool handle_redirects) - { - m_handleRedirects = handle_redirects; - return *this; - } + HttpClient & SetHandleRedirects(bool handle_redirects); - // Synchronous (blocking) call, should be implemented for each platform - // @returns true if connection was made and server returned something (200, 404, etc.). - // @note Implementations should transparently support all needed HTTP redirects - bool RunHttpRequest(); - - string const & url_requested() const { return m_urlRequested; } + string const & UrlRequested() const; // @returns empty string in the case of error - string const & url_received() const { return m_urlReceived; } - bool was_redirected() const { return m_urlRequested != m_urlReceived; } + string const & UrlReceived() const; + bool WasRedirected() const; // Mix of HTTP errors (in case of successful connection) and system-dependent error codes, // in the simplest success case use 'if (200 == client.error_code())' // 200 means OK in HTTP - int error_code() const { return m_errorCode; } - string const & server_response() const { return m_serverResponse; } - string const & http_method() const { return m_httpMethod; } + int ErrorCode() const; + string const & ServerResponse() const; + string const & HttpMethod() const; // Pass this getter's value to the set_cookies() method for easier cookies support in the next request. - string combined_cookies() const - { - if (m_serverCookies.empty()) - { - return m_cookies; - } - if (m_cookies.empty()) - { - return m_serverCookies; - } - return m_serverCookies + "; " + m_cookies; - } + string CombinedCookies() const; // Returns cookie value or empty string if it's not present. - string cookie_by_name(string name) const - { - string const str = combined_cookies(); - name += "="; - auto const cookie = str.find(name); - auto const eq = cookie + name.size(); - if (cookie != string::npos && str.size() > eq) - { - return str.substr(eq, str.find(';', eq) - eq); - } - return string(); - } + string CookieByName(string name) const; private: // Internal helper to convert cookies like this: // "first=value1; expires=Mon, 26-Dec-2016 12:12:32 GMT; path=/, second=value2; path=/, third=value3; " // into this: // "first=value1; second=value2; third=value3" - static string normalize_server_cookies(string && cookies) - { - istringstream is(cookies); - string str, result; - // Split by ", ". Can have invalid tokens here, expires= can also contain a comma. - while (getline(is, str, ',')) - { - size_t const leading = str.find_first_not_of(" "); - if (leading != string::npos) - { - str.substr(leading).swap(str); - } - // In the good case, we have '=' and it goes before any ' '. - auto const eq = str.find('='); - if (eq == string::npos) - { - continue; // It's not a cookie: no valid key value pair. - } - auto const sp = str.find(' '); - if (sp != string::npos && eq > sp) - { - continue; // It's not a cookie: comma in expires date. - } - // Insert delimiter. - if (!result.empty()) - { - result.append("; "); - } - // Read cookie itself. - result.append(str, 0, str.find(";")); - } - return result; - } + static string NormalizeServerCookies(string && cookies); - string m_urlRequested; - // Contains final content's url taking redirects (if any) into an account. - string m_urlReceived; - int m_errorCode = kNotInitialized; - string m_bodyFile; - // Used instead of server_reply_ if set. - string m_receivedFile; - // Data we received from the server if output_file_ wasn't initialized. - string m_serverResponse; - string m_contentType; - string m_contentTypeReceived; - string m_contentEncoding; - string m_contentEncodingReceived; - string m_userAgent; - string m_bodyData; - string m_httpMethod = "GET"; - string m_basicAuthUser; - string m_basicAuthPassword; - // All Set-Cookie values from server response combined in a Cookie format: - // cookie1=value1; cookie2=value2 - // TODO(AlexZ): Support encoding and expiration/path/domains etc. - string m_serverCookies; - // Cookies set by the client before request is run. - string m_cookies; - bool m_debugMode = false; - bool m_handleRedirects = true; + string m_urlRequested; + // Contains final content's url taking redirects (if any) into an account. + string m_urlReceived; + int m_errorCode = kNoError; + string m_inputFile; + // Used instead of server_reply_ if set. + string m_outputFile; + // Data we received from the server if output_file_ wasn't initialized. + string m_serverResponse; + string m_contentType; + string m_contentTypeReceived; + string m_contentEncoding; + string m_contentEncodingReceived; + string m_userAgent; + string m_bodyData; + string m_httpMethod = "GET"; + string m_basicAuthUser; + string m_basicAuthPassword; + // All Set-Cookie values from server response combined in a Cookie format: + // cookie1=value1; cookie2=value2 + // TODO(AlexZ): Support encoding and expiration/path/domains etc. + string m_serverCookies; + // Cookies set by the client before request is run. + string m_cookies; + bool m_debugMode = false; + bool m_handleRedirects = true; - DISALLOW_COPY_AND_MOVE(HttpClient); + DISALLOW_COPY_AND_MOVE(HttpClient); }; + +string DebugPrint(HttpClient const & request); } // namespace platform diff --git a/platform/http_client_apple.mm b/platform/http_client_apple.mm index 9f06f6d5af..4d063c08c2 100644 --- a/platform/http_client_apple.mm +++ b/platform/http_client_apple.mm @@ -54,112 +54,105 @@ static const double kTimeoutInSeconds = 24.0; // TODO(AlexZ): Rewrite to use async implementation for better redirects handling and ability to cancel request from destructor. bool HttpClient::RunHttpRequest() { - @autoreleasepool - { - NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL: - [NSURL URLWithString:[NSString stringWithUTF8String:m_urlRequested.c_str()]] - cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:kTimeoutInSeconds]; - // We handle cookies manually. - request.HTTPShouldHandleCookies = NO; + NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL: + [NSURL URLWithString:[NSString stringWithUTF8String:m_urlRequested.c_str()]] + cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:kTimeoutInSeconds]; + // We handle cookies manually. + request.HTTPShouldHandleCookies = NO; - request.HTTPMethod = [NSString stringWithUTF8String:m_httpMethod.c_str()]; - if (!m_contentType.empty()) - [request setValue:[NSString stringWithUTF8String:m_contentType.c_str()] forHTTPHeaderField:@"Content-Type"]; + request.HTTPMethod = [NSString stringWithUTF8String:m_httpMethod.c_str()]; + if (!m_contentType.empty()) + [request setValue:[NSString stringWithUTF8String:m_contentType.c_str()] forHTTPHeaderField:@"Content-Type"]; - if (!m_contentEncoding.empty()) - [request setValue:[NSString stringWithUTF8String:m_contentEncoding.c_str()] forHTTPHeaderField:@"Content-Encoding"]; + if (!m_contentEncoding.empty()) + [request setValue:[NSString stringWithUTF8String:m_contentEncoding.c_str()] forHTTPHeaderField:@"Content-Encoding"]; - if (!m_userAgent.empty()) - [request setValue:[NSString stringWithUTF8String:m_userAgent.c_str()] forHTTPHeaderField:@"User-Agent"]; + if (!m_userAgent.empty()) + [request setValue:[NSString stringWithUTF8String:m_userAgent.c_str()] forHTTPHeaderField:@"User-Agent"]; - if (!m_cookies.empty()) - [request setValue:[NSString stringWithUTF8String:m_cookies.c_str()] forHTTPHeaderField:@"Cookie"]; + if (!m_cookies.empty()) + [request setValue:[NSString stringWithUTF8String:m_cookies.c_str()] forHTTPHeaderField:@"Cookie"]; #if (TARGET_OS_IPHONE > 0) - else if (gBrowserUserAgent) - [request setValue:gBrowserUserAgent forHTTPHeaderField:@"User-Agent"]; + else if (gBrowserUserAgent) + [request setValue:gBrowserUserAgent forHTTPHeaderField:@"User-Agent"]; #endif // TARGET_OS_IPHONE - if (!m_basicAuthUser.empty()) - { - NSData * loginAndPassword = [[NSString stringWithUTF8String:(m_basicAuthUser + ":" + m_basicAuthPassword).c_str()] dataUsingEncoding:NSUTF8StringEncoding]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - // base64Encoding selector below was deprecated in iOS 7+, but we still need it to support 5.1+ versions. - [request setValue:[NSString stringWithFormat:@"Basic %@", [loginAndPassword base64Encoding]] forHTTPHeaderField:@"Authorization"]; -#pragma clang diagnostic pop - } + if (!m_basicAuthUser.empty()) + { + NSData * loginAndPassword = [[NSString stringWithUTF8String:(m_basicAuthUser + ":" + m_basicAuthPassword).c_str()] dataUsingEncoding:NSUTF8StringEncoding]; + [request setValue:[NSString stringWithFormat:@"Basic %@", [loginAndPassword base64EncodedStringWithOptions:0]] forHTTPHeaderField:@"Authorization"]; + } - if (!m_bodyData.empty()) - { - request.HTTPBody = [NSData dataWithBytes:m_bodyData.data() length:m_bodyData.size()]; - if (m_debugMode) - LOG(LINFO, ("Uploading buffer of size", m_bodyData.size(), "bytes")); - } - else if (!m_bodyFile.empty()) + if (!m_bodyData.empty()) + { + request.HTTPBody = [NSData dataWithBytes:m_bodyData.data() length:m_bodyData.size()]; + if (m_debugMode) + LOG(LINFO, ("Uploading buffer of size", m_bodyData.size(), "bytes")); + } + else if (!m_inputFile.empty()) + { + NSError * err = nil; + NSString * path = [NSString stringWithUTF8String:m_inputFile.c_str()]; + const unsigned long long file_size = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&err].fileSize; + if (err) { - NSError * err = nil; - NSString * path = [NSString stringWithUTF8String:m_bodyFile.c_str()]; - const unsigned long long file_size = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&err].fileSize; - if (err) - { - m_errorCode = static_cast<int>(err.code); - if (m_debugMode) - LOG(LERROR, ("Error: ", m_errorCode, [err.localizedDescription UTF8String])); - - return false; - } - request.HTTPBodyStream = [NSInputStream inputStreamWithFileAtPath:path]; - [request setValue:[NSString stringWithFormat:@"%llu", file_size] forHTTPHeaderField:@"Content-Length"]; + m_errorCode = static_cast<int>(err.code); if (m_debugMode) - LOG(LINFO, ("Uploading file", m_bodyFile, file_size, "bytes")); + LOG(LERROR, ("Error: ", m_errorCode, [err.localizedDescription UTF8String])); + + return false; } + request.HTTPBodyStream = [NSInputStream inputStreamWithFileAtPath:path]; + [request setValue:[NSString stringWithFormat:@"%llu", file_size] forHTTPHeaderField:@"Content-Length"]; + if (m_debugMode) + LOG(LINFO, ("Uploading file", m_inputFile, file_size, "bytes")); + } - NSHTTPURLResponse * response = nil; - NSError * err = nil; - NSData * url_data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err]; + NSHTTPURLResponse * response = nil; + NSError * err = nil; + NSData * url_data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err]; - if (response) - { - m_errorCode = static_cast<int>(response.statusCode); - m_urlReceived = [response.URL.absoluteString UTF8String]; - - NSString * content = [response.allHeaderFields objectForKey:@"Content-Type"]; - if (content) - m_contentTypeReceived = std::move([content UTF8String]); - - NSString * encoding = [response.allHeaderFields objectForKey:@"Content-Encoding"]; - if (encoding) - m_contentEncodingReceived = std::move([encoding UTF8String]); - - // Apple merges all Set-Cookie fields into one NSDictionary key delimited by commas. - NSString * cookies = [response.allHeaderFields objectForKey:@"Set-Cookie"]; - if (cookies) - m_serverCookies = normalize_server_cookies(std::move([cookies UTF8String])); - - if (url_data) - { - if (m_receivedFile.empty()) - m_serverResponse.assign(reinterpret_cast<char const *>(url_data.bytes), url_data.length); - else - [url_data writeToFile:[NSString stringWithUTF8String:m_receivedFile.c_str()] atomically:YES]; - - } - return true; - } - // Request has failed if we are here. - // MacOSX/iOS-specific workaround for HTTP 401 error bug. - // @see bit.ly/1TrHlcS for more details. - if (err.code == NSURLErrorUserCancelledAuthentication) + if (response) + { + m_errorCode = static_cast<int>(response.statusCode); + m_urlReceived = [response.URL.absoluteString UTF8String]; + + NSString * content = [response.allHeaderFields objectForKey:@"Content-Type"]; + if (content) + m_contentTypeReceived = std::move([content UTF8String]); + + NSString * encoding = [response.allHeaderFields objectForKey:@"Content-Encoding"]; + if (encoding) + m_contentEncodingReceived = std::move([encoding UTF8String]); + + // Apple merges all Set-Cookie fields into one NSDictionary key delimited by commas. + NSString * cookies = [response.allHeaderFields objectForKey:@"Set-Cookie"]; + if (cookies) + m_serverCookies = NormalizeServerCookies(std::move([cookies UTF8String])); + + if (url_data) { - m_errorCode = 401; - return true; + if (m_outputFile.empty()) + m_serverResponse.assign(reinterpret_cast<char const *>(url_data.bytes), url_data.length); + else + [url_data writeToFile:[NSString stringWithUTF8String:m_outputFile.c_str()] atomically:YES]; + } + return true; + } + // Request has failed if we are here. + // MacOSX/iOS-specific workaround for HTTP 401 error bug. + // @see bit.ly/1TrHlcS for more details. + if (err.code == NSURLErrorUserCancelledAuthentication) + { + m_errorCode = 401; + return true; + } - m_errorCode = static_cast<int>(err.code); - if (m_debugMode) - LOG(LERROR, ("Error: ", m_errorCode, ':', [err.localizedDescription UTF8String], "while connecting to", m_urlRequested)); + m_errorCode = static_cast<int>(err.code); + if (m_debugMode) + LOG(LERROR, ("Error: ", m_errorCode, ':', [err.localizedDescription UTF8String], "while connecting to", m_urlRequested)); - return false; - } // @autoreleasepool + return false; } } // namespace platform diff --git a/platform/http_client_curl.cpp b/platform/http_client_curl.cpp index bdf116a3b1..5f2665e403 100644 --- a/platform/http_client_curl.cpp +++ b/platform/http_client_curl.cpp @@ -53,15 +53,15 @@ DECLARE_EXCEPTION(PipeCallError, RootException); struct ScopedRemoveFile { ScopedRemoveFile() = default; - explicit ScopedRemoveFile(string const & fileName) : m_file(fileName) {} + explicit ScopedRemoveFile(string const & fileName) : m_fileName(fileName) {} ~ScopedRemoveFile() { - if (!m_file.empty()) - std::remove(m_file.c_str()); + if (!m_fileName.empty()) + std::remove(m_fileName.c_str()); } - std::string m_file; + std::string m_fileName; }; static string ReadFileAsString(string const & filePath) @@ -111,9 +111,9 @@ string GetTmpFileName() return GetPlatform().TmpPathForFile(ss.str()); } -typedef vector<pair<string, string>> HeadersT; +typedef vector<pair<string, string>> Headers; -HeadersT ParseHeaders(string const & raw) +Headers ParseHeaders(string const & raw) { istringstream stream(raw); HeadersT headers; @@ -130,6 +130,19 @@ HeadersT ParseHeaders(string const & raw) } return headers; } + +bool WriteToFile(string const & fileName, string const & data) +{ + ofstream ofs(fileName); + if(!ofs.is_open()) + { + LOG(LERROR, ("Failed to write into a temporary file.")); + return false; + } + + ofs << data; + return true; +} } // namespace // Used as a test stub for basic HTTP client implementation. // Make sure that you have curl installed in the PATH. @@ -145,7 +158,7 @@ bool HttpClient::RunHttpRequest() ScopedRemoveFile body_deleter; ScopedRemoveFile received_file_deleter; - string cmd = "curl -s -w '%{http_code}' -X " + m_httpMethod + " -D '" + headers_deleter.m_file + "' "; + string cmd = "curl -s -w '%{http_code}' -X " + m_httpMethod + " -D '" + headers_deleter.m_fileName + "' "; if (!m_contentType.empty()) cmd += "-H 'Content-Type: " + m_contentType + "' "; @@ -161,27 +174,25 @@ bool HttpClient::RunHttpRequest() if (!m_bodyData.empty()) { - body_deleter.m_file = GetTmpFileName(); + body_deleter.m_fileName = GetTmpFileName(); // POST body through tmp file to avoid breaking command line. - if (!(ofstream(body_deleter.m_file) << m_bodyData).good()) - { - LOG(LERROR, ("Failed to write into a temporary file.")); + if (!WriteToFile(body_deleter.m_fileName, m_bodyData)) return false; - } + // TODO(AlexZ): Correctly clean up this internal var to avoid client confusion. - m_bodyFile = body_deleter.m_file; + m_inputFile = body_deleter.m_fileName; } // Content-Length is added automatically by curl. - if (!m_bodyFile.empty()) - cmd += "--data-binary '@" + m_bodyFile + "' "; + if (!m_inputFile.empty()) + cmd += "--data-binary '@" + m_inputFile + "' "; // Use temporary file to receive data from server. // If user has specified file name to save data, it is not temporary and is not deleted automatically. - string rfile = m_receivedFile; + string rfile = m_outputFile; if (rfile.empty()) { rfile = GetTmpFileName(); - received_file_deleter.m_file = rfile; + received_file_deleter.m_fileName = rfile; } cmd += "-o " + rfile + strings::to_string(" ") + "'" + m_urlRequested + "'"; @@ -202,7 +213,7 @@ bool HttpClient::RunHttpRequest() return false; } - HeadersT const headers = ParseHeaders(ReadFileAsString(headers_deleter.m_file)); + HeadersT const headers = ParseHeaders(ReadFileAsString(headers_deleter.m_fileName)); for (auto const & header : headers) { if (header.first == "Set-Cookie") @@ -222,14 +233,14 @@ bool HttpClient::RunHttpRequest() m_urlReceived = header.second; } } - m_serverCookies = normalize_server_cookies(move(m_serverCookies)); + m_serverCookies = NormalizeServerCookies(move(m_serverCookies)); if (m_urlReceived.empty()) { m_urlReceived = m_urlRequested; // Load body contents in final request only (skip redirects). // Sometimes server can reply with empty body, and it's ok. - if (m_receivedFile.empty()) + if (m_outputFile.empty()) m_serverResponse = ReadFileAsString(rfile); } else @@ -240,7 +251,7 @@ bool HttpClient::RunHttpRequest() LOG(LINFO, ("HTTP redirect", m_errorCode, "to", m_urlReceived)); HttpClient redirect(m_urlReceived); - redirect.set_cookies(combined_cookies()); + redirect.SetCookies(CombinedCookies()); if (!redirect.RunHttpRequest()) { @@ -248,8 +259,8 @@ bool HttpClient::RunHttpRequest() return false; } - m_errorCode = redirect.error_code(); - m_urlReceived = redirect.url_received(); + m_errorCode = redirect.ErrorCode(); + m_urlReceived = redirect.UrlReceived(); m_serverCookies = move(redirect.m_serverCookies); m_serverResponse = move(redirect.m_serverResponse); m_contentTypeReceived = move(redirect.m_contentTypeReceived); diff --git a/platform/platform.pro b/platform/platform.pro index b623316b02..66f5e60527 100644 --- a/platform/platform.pro +++ b/platform/platform.pro @@ -92,6 +92,7 @@ SOURCES += \ country_file.cpp \ file_logging.cpp \ get_text_by_id.cpp \ + http_client.cpp \ http_request.cpp \ local_country_file.cpp \ local_country_file_utils.cpp \ diff --git a/platform/platform_mac.mm b/platform/platform_mac.mm index 1d509cf925..062e06b64b 100644 --- a/platform/platform_mac.mm +++ b/platform/platform_mac.mm @@ -19,8 +19,6 @@ Platform::Platform() { - @autoreleasepool - { // get resources directory path string const resourcesPath = [[[NSBundle mainBundle] resourcePath] UTF8String]; string const bundlePath = [[[NSBundle mainBundle] bundlePath] UTF8String]; @@ -78,7 +76,6 @@ Platform::Platform() LOG(LDEBUG, ("Writable Directory:", m_writableDir)); LOG(LDEBUG, ("Tmp Directory:", m_tmpDir)); LOG(LDEBUG, ("Settings Directory:", m_settingsDir)); - } // @autoreleasepool } string Platform::UniqueClientId() const |