Skip to content

Commit

Permalink
Unify handling of tolower operations to avoid locale sensitivity. (#822)
Browse files Browse the repository at this point in the history
  • Loading branch information
BillyONeal authored and ras0219-msft committed Aug 2, 2018
1 parent c55ada9 commit a6b4153
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 149 deletions.
83 changes: 38 additions & 45 deletions Release/include/cpprest/asyncrt_utils.h
Expand Up @@ -29,7 +29,6 @@

#ifndef _WIN32
#include <sys/time.h>
#include <boost/algorithm/string.hpp>
#if !defined(ANDROID) && !defined(__ANDROID__) && defined(HAVE_XLOCALE_H) // CodePlex 269
/* Systems using glibc: xlocale.h has been removed from glibc 2.26
The above include of locale.h is sufficient
Expand Down Expand Up @@ -428,19 +427,48 @@ namespace details
}

/// <summary>
/// Cross platform utility function for performing case insensitive string comparision.
/// Cross platform utility function for performing case insensitive string equality comparision.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if the strings are equivalent, false otherwise</returns>
inline bool str_icmp(const utility::string_t &left, const utility::string_t &right)
{
#ifdef _WIN32
return _wcsicmp(left.c_str(), right.c_str()) == 0;
#else
return boost::iequals(left, right);
#endif
}
_ASYNCRTIMP bool __cdecl str_iequal(const std::string &left, const std::string &right) CPPREST_NOEXCEPT;

/// <summary>
/// Cross platform utility function for performing case insensitive string equality comparision.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if the strings are equivalent, false otherwise</returns>
_ASYNCRTIMP bool __cdecl str_iequal(const std::wstring &left, const std::wstring &right) CPPREST_NOEXCEPT;

/// <summary>
/// Cross platform utility function for performing case insensitive string less-than comparision.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise, false.</returns>
_ASYNCRTIMP bool __cdecl str_iless(const std::string &left, const std::string &right) CPPREST_NOEXCEPT;

/// <summary>
/// Cross platform utility function for performing case insensitive string less-than comparision.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise, false.</returns>
_ASYNCRTIMP bool __cdecl str_iless(const std::wstring &left, const std::wstring &right) CPPREST_NOEXCEPT;

/// <summary>
/// Convert a string to lowercase in place.
/// </summary>
/// <param name="target">The string to convert to lowercase.</param>
_ASYNCRTIMP void __cdecl inplace_tolower(std::string &target) CPPREST_NOEXCEPT;

/// <summary>
/// Convert a string to lowercase in place.
/// </summary>
/// <param name="target">The string to convert to lowercase.</param>
_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring &target) CPPREST_NOEXCEPT;

#ifdef _WIN32

Expand Down Expand Up @@ -642,41 +670,6 @@ class datetime
interval_type m_interval;
};

#ifndef _WIN32

// temporary workaround for the fact that
// utf16char is not fully supported in GCC
class cmp
{
public:

static int icmp(std::string left, std::string right)
{
size_t i;
for (i = 0; i < left.size(); ++i)
{
if (i == right.size()) return 1;

auto l = cmp::tolower(left[i]);
auto r = cmp::tolower(right[i]);
if (l > r) return 1;
if (l < r) return -1;
}
if (i < right.size()) return -1;
return 0;
}

private:
static char tolower(char c)
{
if (c >= 'A' && c <= 'Z')
return static_cast<char>(c - 'A' + 'a');
return c;
}
};

#endif

inline int operator- (datetime t1, datetime t2)
{
auto diff = (t1.m_interval - t2.m_interval);
Expand Down
40 changes: 20 additions & 20 deletions Release/include/cpprest/http_headers.h
Expand Up @@ -68,30 +68,30 @@ class http_headers
{
bool operator()(const utility::string_t &str1, const utility::string_t &str2) const
{
#ifdef _WIN32
return _wcsicmp(str1.c_str(), str2.c_str()) < 0;
#else
return utility::cmp::icmp(str1, str2) < 0;
#endif
return utility::details::str_iless(str1, str2);
}
};

private:
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp> inner_container;
public:

/// <summary>
/// STL-style typedefs
/// </summary>
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::key_type key_type;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::key_compare key_compare;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::allocator_type allocator_type;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::size_type size_type;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::difference_type difference_type;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::pointer pointer;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::const_pointer const_pointer;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::reference reference;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::const_reference const_reference;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::iterator iterator;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::const_iterator const_iterator;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::reverse_iterator reverse_iterator;
typedef std::map<utility::string_t, utility::string_t, _case_insensitive_cmp>::const_reverse_iterator const_reverse_iterator;
typedef typename inner_container::key_type key_type;
typedef typename inner_container::key_compare key_compare;
typedef typename inner_container::allocator_type allocator_type;
typedef typename inner_container::size_type size_type;
typedef typename inner_container::difference_type difference_type;
typedef typename inner_container::pointer pointer;
typedef typename inner_container::const_pointer const_pointer;
typedef typename inner_container::reference reference;
typedef typename inner_container::const_reference const_reference;
typedef typename inner_container::iterator iterator;
typedef typename inner_container::const_iterator const_iterator;
typedef typename inner_container::reverse_iterator reverse_iterator;
typedef typename inner_container::const_reverse_iterator const_reverse_iterator;

/// <summary>
/// Constructs an empty set of HTTP headers.
Expand Down Expand Up @@ -318,7 +318,7 @@ class http_headers
}

// Headers are stored in a map with case insensitive key.
std::map<utility::string_t, utility::string_t, _case_insensitive_cmp> m_headers;
inner_container m_headers;
};

}}
}}
38 changes: 18 additions & 20 deletions Release/samples/BlackJack/BlackJack_Client/BlackJackClient.cpp
Expand Up @@ -13,17 +13,12 @@
#include <iostream>
#include <streambuf>
#include <sstream>
#include <locale>
#include <fstream>
#include "../BlackJack_Server/messagetypes.h"

#ifdef _WIN32
# define iequals(x, y) (_stricmp((x), (y))==0)
#else
# define iequals(x, y) boost::iequals((x), (y))
#endif

using namespace std;
using namespace web;
using namespace web;
using namespace utility;
using namespace http;
using namespace http::client;
Expand Down Expand Up @@ -134,7 +129,7 @@ void PrintTable(const http_response &response, bool &refresh)
}
}

//
//
// Entry point for the blackjack client.
// Arguments: BlackJack_Client.exe <port>
// If port is not specified, client will assume that the server is listening on port 34568
Expand Down Expand Up @@ -179,7 +174,11 @@ int main(int argc, char *argv[])
ucout << "Enter method:";
cin >> method;

if ( iequals(method.c_str(), "quit") )
const auto methodFirst = &method[0];
const auto methodLast = methodFirst + method.size();
std::use_facet<std::ctype<char>>(std::locale::classic()).tolower(methodFirst, methodLast);

if (method == "quit")
{
if ( !userName.empty() && !table.empty() )
{
Expand All @@ -190,12 +189,12 @@ int main(int argc, char *argv[])
break;
}

if ( iequals(method.c_str(), "name") )
if (method == "name")
{
ucout << "Enter user name:";
ucin >> userName;
}
else if ( iequals(method.c_str(), "join") )
else if (method == "join")
{
ucout << "Enter table name:";
ucin >> table;
Expand All @@ -206,16 +205,16 @@ int main(int argc, char *argv[])
buf << table << U("?name=") << userName;
CheckResponse("blackjack/dealer", bjDealer.request(methods::POST, buf.str()).get(), was_refresh);
}
else if ( iequals(method.c_str(), "hit")
|| iequals(method.c_str(), "stay")
|| iequals(method.c_str(), "double") )
else if (method == "hit"
|| method == "stay"
|| method == "double")
{
utility::ostringstream_t buf;
buf << table << U("?request=") << utility::conversions::to_string_t(method) << U("&name=") << userName;
PrintTable(CheckResponse("blackjack/dealer", bjDealer.request(methods::PUT, buf.str()).get()), was_refresh);
}
else if ( iequals(method.c_str(), "bet")
|| iequals(method.c_str(), "insure") )
else if (method == "bet"
||method == "insure")
{
utility::string_t bet;
ucout << "Enter bet:";
Expand All @@ -227,11 +226,11 @@ int main(int argc, char *argv[])
buf << table << U("?request=") << utility::conversions::to_string_t(method) << U("&name=") << userName << U("&amount=") << bet;
PrintTable(CheckResponse("blackjack/dealer", bjDealer.request(methods::PUT, buf.str()).get()), was_refresh);
}
else if ( iequals(method.c_str(), "newtbl") )
else if (method == "newtbl")
{
CheckResponse("blackjack/dealer", bjDealer.request(methods::POST).get(), was_refresh);
}
else if ( iequals(method.c_str(), "leave") )
else if (method == "leave")
{
ucout << "Enter table:";
ucin >> table;
Expand All @@ -242,7 +241,7 @@ int main(int argc, char *argv[])
buf << table << U("?name=") << userName;
CheckResponse("blackjack/dealer", bjDealer.request(methods::DEL, buf.str()).get(), was_refresh);
}
else if ( iequals(method.c_str(), "list") )
else if (method == "list")
{
was_refresh = false;
http_response response = CheckResponse("blackjack/dealer", bjDealer.request(methods::GET).get());
Expand All @@ -268,4 +267,3 @@ int main(int argc, char *argv[])

return 0;
}

0 comments on commit a6b4153

Please sign in to comment.